## Load libraries
suppressPackageStartupMessages(library(ArchR))
library(knitr)
library(rhdf5)
library(uwot)
library(tidyverse)
library(scater)
library(zellkonverter)
library(corrplot)
#library(caret)
h5disableFileLocking()
The scATAC-seq dataset contains two replicates each from 4 stages of mouse gastrulation, E7.5, E8.0, E8.5 and E8.75. We will use the mm10 Genome, because that is the genome.
# names of gastrulation stages
stages <- c("E7.5_rep1", "E7.5_rep2", "E8.0_rep1", "E8.0_rep2",
"E8.5_rep1", "E8.5_rep2", "E8.75_rep1", "E8.75_rep2")
# combine all raw fragment files
inputFiles <- setNames(c("raw_files/E7.5_rep1_atac_fragments.tsv.gz",
"raw_files/E7.5_rep2_atac_fragments.tsv.gz",
"raw_files/E8.0_rep1_atac_fragments.tsv.gz",
"raw_files/E8.0_rep2_atac_fragments.tsv.gz",
"raw_files/E8.5_rep1_atac_fragments.tsv.gz",
"raw_files/E8.5_rep2_atac_fragments.tsv.gz",
"raw_files/E8.75_rep1_atac_fragments.tsv.gz",
"raw_files/E8.75_rep2_atac_fragments.tsv.gz"),
c("E7.5_rep1", "E7.5_rep2", "E8.0_rep1", "E8.0_rep2",
"E8.5_rep1", "E8.5_rep2", "E8.75_rep1", "E8.75_rep2"))
addArchRGenome("mm10")
Create Arrow Files
We will read in the accessible fragments from the fragments file. QC information is automatically computed for each cell. Based on visual inspection of TSS Enrichment score vs log10(nFrags) I used a threshold of at least 1000 unique fragments and a TSS Enrichment score of at least 2.75. At the beginning it is better to be less stringent, because we can still filter the data at a later stage. The cells are filtered based on these threshold and data are stored in arrow files. Genome-wide tile matrices are created using 500-bp bins. The tile matrix T is binary matrix and contains information whether in a particular 500-bp bin/tile i there were any insertions in a particular cell j.
\(T \in \mathbb{R}^{NXD}\) with \(T_{i,j} = 1\) if an insertion is present in tile \(i\) and cell \(j\). \(T_{i,j} = 0\) if no insertion is present in that cell, meaning \(T\) is a binary matrix.
In addition a GeneScoreMatrix is created.
I set the size of the maximum number of fragments included to 1e+07, instead of the default 1e+05, because we cut off some cells otherwise.
ArrowFiles <- createArrowFiles(
inputFiles = inputFiles,
sampleNames = names(inputFiles),
minTSS =2.75, #Dont set this too high because you can always increase later
minFrags = 1000, # minimum number of mapped fragments required
# count matrix, instead of using peak it uses fixed-width sliding window of bins across the whole genome
addTileMat = TRUE,
addGeneScoreMat = TRUE, # uses signal proximal to the TSS to estimate gene activity
subThreading = FALSE,
maxFrags = 1e+07,
minFragSize = 10,
maxFragSize = 2000,
QCDir = "new_ArchRobject/QualityControl",
force = FALSE, # change to TRUE if you want to ovewrite the files
# the length in bp that wraps around nucleosomes ->
#identify fragments as sub-nucleosome spanning, mono-nucleosome spanning or multi-nucleosome spanning
nucLength = 147,
# integer vector -> define region up/downstream of TSS to include as promoter region
# can be used to calculate e.g fraction of reads in promoter (FIP)
promoterRegion = c(2000, 100),
# parameters for computing TSS enrichment scores, window (bp) centered at TSS = 101
# flanking window = 2000 bp
# norm = size of flank window used for normalization = 100 bp
# accessibility within 101 bp surrounding the TSS will be normalized to accessibility
# in 100 bp bins from -2000:-1901bp and 1901: 2000
TSSParams = list(101, 2000, 100),
# which chromosomes to exclude form downstream analysis
# in human and mouse: mitochondrial DNA (chrM) and male sex chromosome (chrY)
# the fragments are still stored in the arrow files
excludeChr = c("chrM", "chrY"),
# number of chunks to divide chromosomes in -> low-memory parallelized reading of input files
nChunk = 5,
# name of field in input bam file containing the barcode tag information
bcTag = "qname",
offsetPlus = 4, # offset applied to + stranded Tn5 insertion -> account for precise Tn5 binding site
offsetMinus = -5,
logFile = createLogFile("createArrows")
)
Inferring Doublets
Should be removed, because they can interfere with downstream analysis.
Doublet detection-and-removal algorithm: Heterotypic doublets are identified by generating a collection of synthetic doublets. These synthetic doublets are projected into low-dimensional embeddings. Searching for nearest neighbours to the synthetic doublets we can identify doublets in the dataset. This outperforms the prediction of doublets using fragment number (ROC-AUC). (Compared to demuxlet as ground truth)
We can also identify doublets in the scRNA-seq space if we have paired data and remove the cells in this way.
ArrowFiles <- setNames(c("E7.5_rep1.arrow", "E7.5_rep2.arrow", "E8.0_rep1.arrow",
"E8.0_rep2.arrow", "E8.5_rep1.arrow", "E8.5_rep2.arrow",
"E8.75_rep1.arrow", "E8.75_rep2.arrow"),
c("E7.5_rep1", "E7.5_rep2", "E8.0_rep1", "E8.0_rep2",
"E8.5_rep1", "E8.5_rep2", "E8.75_rep1", "E8.75_rep2"))
# for each sample provided doublet information will be assigned to each cell
# this way we can remove doublet-based clusters downstream
doubScores <- addDoubletScores(
useMatrix = "TileMatrix",
input = ArrowFiles,
k = 10, #Refers to how many cells near a "pseudo-doublet" to count.
nTrial = 5, # number of time to simulate nCell doublets
knnMethod = "UMAP", #Refers to the dimensionality reduciton method to use for nearest neighbor search.
LSIMethod = 1 # oder of normalization: tf-log(idf)
)
Create ArchRProject
An ArchR Project is initialized with some important attributes:
- ouput directory
- sample names
sampleColData -> matrix containing data for each sample
cellColData -> contains data associated with each cell
- after using
addDoubletScore() there will be a column for “Doublet Enrichment” and “Doublet Score”
- total number of cells (excluding doublets)
- median TSS score & median number of fragments across all cells and samples
proj <- ArchRProject(
ArrowFiles = ArrowFiles,
outputDirectory = "gastrArchROutput",
copyArrows = TRUE, #This is recommened so that you maintain an unaltered copy for later usage.
geneAnnotation = getGeneAnnotation(),
#genomeAnnotation = getGeneAnnotation(),
showLogo = FALSE
)
proj
proj <- loadArchRProject("08_ArchRobject_all_timepoints_added_gene_expr/")
# read in existing project, so we don't have to rerun the creation
#saveArchRProject(proj, outputDirectory = "01_FirstQC_ArchRobject/", load = FALSE)
#proj <- loadArchRProject(path = "01_FirstQC_ArchRobject/")
Quality Control
Have a look at this for additional QC! https://bioconductor.org/packages/devel/bioc/vignettes/ATACseqQC/inst/doc/ATACseqQC.html
- the number of unique nuclear fragments (as opposed to mitochondrial fragments) A cell with very few usable fragments will not provide enough data to make useful conclusions.
- signal-to-background ratio: if this is low this probably corresponds to dying cells where the entire genome allows random transposition
- fragment size distribution: since 147 bp are wrapped around a nucleosome it is expected that there are depletions of fragments of this length at regular intervals. We expect to see a periodic distribution of fragmetn size corresponding to nucleosomes (mono, di, tri, …), because Tn5 cannot cut DNA that is tightly wrapped around a nucleosome.
Data before QC and corresponding plots are saved in the Quality Control output folder.
log10(unique fragments) vs TSS enrichment
before QC
We used a TSS enrichment score of > 2.75 and a number of unique fragments > 1000 (log10(1000) = 3). It might make sense to use different thresholds for different samples.
dir <- "QualityControl/"
#all_files <- list.files(path)
beforeQC <- map(stages, function(n) {
path <- paste0(dir, n, "/")
all_files <- list.files(path)
file <- all_files[grepl(paste0(n, "-Pre-Filter-Metadata.rds"), all_files)]
# read in file for sample n containing original data before QC
df <- as.data.frame(readRDS(paste0(dir, n, "/", file)))
df %>% mutate(Sample = n) %>%
ggplot(aes(x = log10(nFrags), y = TSSEnrichment)) +
geom_density2d_filled() +
geom_hline(yintercept = 2.75, color="green", linetype="dashed") +
geom_vline(xintercept = log10(1000), color="green", linetype="dashed") +
#geom_xsidedensity(aes(x=log10(pre_filter_meta$nFrags))) +
#geom_ysidedensity(aes(y = pre_filter_meta$TSSEnrichment)) +
ylim(c(2.5, 4.5)) +
facet_wrap(~Sample) +
theme(legend.position = "none") +
labs(x = "Log10 Unique Fragments", y = "TSS Enrichment Score")
})
gridExtra::grid.arrange(grobs = beforeQC, ncol = 3)
## Warning: Removed 361 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 382 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 124 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 103 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 75 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 62 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 269 rows containing non-finite values (stat_density2d_filled).
## Warning: Removed 277 rows containing non-finite values (stat_density2d_filled).

after QC
metadata <- as.data.frame(getCellColData(proj))
metadata %>% head
## Sample TSSEnrichment ReadsInTSS ReadsInPromoter
## E7.5_rep1#GTATTGCAGCGGATTT-1 E7.5_rep1 3.080 9360 41754
## E7.5_rep1#GCTGTACCACTATGGC-1 E7.5_rep1 3.396 13022 53683
## E7.5_rep1#AGGTTTGGTAGGTTTG-1 E7.5_rep1 3.016 8528 37914
## E7.5_rep1#CACAATATCGGGATTT-1 E7.5_rep1 3.506 11438 45668
## E7.5_rep1#TGTAACTCAAGACTCC-1 E7.5_rep1 3.261 10098 42865
## E7.5_rep1#ATTGGCTAGGCATTAC-1 E7.5_rep1 3.121 7809 34552
## ReadsInBlacklist PromoterRatio PassQC
## E7.5_rep1#GTATTGCAGCGGATTT-1 4831 0.1239800 1
## E7.5_rep1#GCTGTACCACTATGGC-1 5458 0.1661714 1
## E7.5_rep1#AGGTTTGGTAGGTTTG-1 5621 0.1282907 1
## E7.5_rep1#CACAATATCGGGATTT-1 5072 0.1627315 1
## E7.5_rep1#TGTAACTCAAGACTCC-1 4411 0.1639598 1
## E7.5_rep1#ATTGGCTAGGCATTAC-1 5084 0.1334044 1
## NucleosomeRatio nMultiFrags nMonoFrags nFrags
## E7.5_rep1#GTATTGCAGCGGATTT-1 1.368854 34465 71085 168390
## E7.5_rep1#GCTGTACCACTATGGC-1 1.327239 32948 69408 161529
## E7.5_rep1#AGGTTTGGTAGGTTTG-1 1.485760 32186 59445 147766
## E7.5_rep1#CACAATATCGGGATTT-1 1.396860 28613 58542 140317
## E7.5_rep1#TGTAACTCAAGACTCC-1 1.316832 27106 56421 130718
## E7.5_rep1#ATTGGCTAGGCATTAC-1 2.599450 28797 35978 129501
## nDiFrags DoubletScore DoubletEnrichment
## E7.5_rep1#GTATTGCAGCGGATTT-1 62840 138.38655 6.700
## E7.5_rep1#GCTGTACCACTATGGC-1 59173 1.53785 1.750
## E7.5_rep1#AGGTTTGGTAGGTTTG-1 56135 14.75493 2.600
## E7.5_rep1#CACAATATCGGGATTT-1 53162 323.30622 10.925
## E7.5_rep1#TGTAACTCAAGACTCC-1 47191 0.00000 0.400
## E7.5_rep1#ATTGGCTAGGCATTAC-1 64726 67.55402 4.650
## BlacklistRatio celltypes Gex_nUMI
## E7.5_rep1#GTATTGCAGCGGATTT-1 0.01434468 Gut 5872
## E7.5_rep1#GCTGTACCACTATGGC-1 0.01689480 Gut 14826
## E7.5_rep1#AGGTTTGGTAGGTTTG-1 0.01901994 Rostral_neurectoderm 3992
## E7.5_rep1#CACAATATCGGGATTT-1 0.01807336 ExE_ectoderm 19045
## E7.5_rep1#TGTAACTCAAGACTCC-1 0.01687220 Epiblast 12849
## E7.5_rep1#ATTGGCTAGGCATTAC-1 0.01962919 ExE_ectoderm 3824
## Gex_nGenes Gex_MitoRatio Gex_RiboRatio
## E7.5_rep1#GTATTGCAGCGGATTT-1 3328 0 0
## E7.5_rep1#GCTGTACCACTATGGC-1 5295 0 0
## E7.5_rep1#AGGTTTGGTAGGTTTG-1 2508 0 0
## E7.5_rep1#CACAATATCGGGATTT-1 6189 0 0
## E7.5_rep1#TGTAACTCAAGACTCC-1 4926 0 0
## E7.5_rep1#ATTGGCTAGGCATTAC-1 2319 0 0
metadata %>% group_by(Sample) %>% summarise(n = n())
## # A tibble: 8 × 2
## Sample n
## <chr> <int>
## 1 E7.5_rep1 6675
## 2 E7.5_rep2 7955
## 3 E8.0_rep1 5154
## 4 E8.0_rep2 4526
## 5 E8.5_rep1 7702
## 6 E8.5_rep2 6310
## 7 E8.75_rep1 3279
## 8 E8.75_rep2 4385
metadata %>% ggplot() +
geom_density2d_filled(aes(x = log10(nFrags), y = TSSEnrichment)) +
geom_hline(yintercept = 2.75, color="green", linetype="dashed") +
geom_vline(xintercept = log10(1000), color="green", linetype="dashed") +
#geom_xsidedensity(aes(x=log10(pre_filter_meta$nFrags))) +
#geom_ysidedensity(aes(y = pre_filter_meta$TSSEnrichment)) +
ylim(c(2.5, 4.5)) +
facet_wrap(~Sample) +
theme(legend.position = "none") +
labs(x = "Log10 Unique Fragments", y = "TSS Enrichment Score")
## Warning: Removed 132 rows containing non-finite values (stat_density2d_filled).

ArchR plots
```#{r, fig.width=8, fig.height=15} #create 3 separate dataframes for all samples samples <- map(unique(proj\(Sample), function(name){ index <- BiocGenerics::which(proj\)Sample %in% name) cells <- proj$cellNames[index] sample_subset <- proj[cells] df <- getCellColData(sample_subset, select = c(“log10(nFrags)”, “TSSEnrichment”)) p <- ggPoint( x = df[, 1], y = df[, 2], colorDensity = TRUE, # should the density of points on the plot be indicated by color? continuousSet = “sambaNight”, xlabel = “Log10 unique fragments”, ylabel = “TSS enrichment”, title = paste0(“Sample:”, name), #xlim = c(log10(500), quantile(df[,1], probs = 0.99)), #ylim = c(0, quantile(df[,2], probs = 0.99)) ) + geom_hline(yintercept = 2.75, lty = “dashed”) + geom_vline(xintercept = 3, lty = “dashed”) })
#do.call(“grid.arrange”, c(samples, ncol=4))
gridExtra::grid.arrange(grobs = samples, ncol = 2)
### Plotting sample statistics
* when we have distinct samples, it can be important to compare various
metric between samples
* ridge plots & violin plots are used for grouped data
```r
p1 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_")) %>%
ggplot() +
geom_density(aes(x = TSSEnrichment, fill = Sample), alpha = 0.8)
## Warning: `as_data_frame()` was deprecated in tibble 2.0.0.
## Please use `as_tibble()` instead.
## The signature and semantics have changed, see `?as_tibble`.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
p2 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_")) %>%
ggplot() +
ggridges::geom_density_ridges(aes(x = TSSEnrichment, y = Sample,
fill = Sample), alpha = 0.8) +
theme(legend.position = "none")
p3 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_")) %>%
ggplot() +
geom_violin(aes(x = Sample, y = TSSEnrichment, fill = Sample), alpha = 0.8) +
geom_boxplot(aes(x = Sample, y = TSSEnrichment,fill = Sample), alpha = 0.4) +
theme(legend.position = "none") +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none") +
labs(title = "TSS Enrichment")
cowplot::plot_grid(p3, p2, p1, ncol = 3)

p1 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>%
ggplot() +
geom_density(aes(x = log10_nFrags, fill = Sample), alpha = 0.8)
p2 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>%
ggplot() +
ggridges::geom_density_ridges(aes(x = log10_nFrags, y = Sample,
fill = Sample), alpha = 0.8) +
theme(legend.position = "none")
p3 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>%
ggplot() +
geom_violin(aes(x = Sample, y = log10_nFrags, fill = Sample), alpha = 0.8) +
geom_boxplot(aes(x = Sample, y = log10_nFrags,fill = Sample), alpha = 0.4) +
theme(legend.position = "none") +
labs(title = "number of fragments") +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none")
cowplot::plot_grid(p3, p2, p1, ncol = 3)

Plot Fragment Size Distribution & TSS Enrichment Profiles
- the distribution of fragments size can be very different between samples, cell types and batches -> these differences do not necessarily correlate with differences in quality
- the dip is the fragment size of a nucleosome ~147bp
- TSS enrichment profiles
- clear peak in the center
- smaller shoulder peak right of the center caused by well positioned +1 nucleosome
#{r, fig.width=5, fig.height=5} p1 <- plotFragmentSizes(ArchRProj = proj) p2 <- plotTSSEnrichment(ArchRProj = proj) ggAlignPlots(p1, p2, type = "v")
Filtering Doublets
In the plots below the Doublet Enrichment represents the enrichment of simulated dobulets nearby each cell, compared to the expected number of doublets if a uniform distriution is assumed.
The doublet density vissualizes where the synthetic doublets are located after projecting them into the 2D embedding.
TODO: Chage path and directory to different name, because the are functions!
# the doublet information is saved in a simpleListobject
dir <- "QualityControl/"
doublet_enrichment <- map(stages, function(n) {
path <- paste0(dir, n, "/")
all_files <- list.files(path)
file <- all_files[grepl(paste0(n, "-Doublet-Summary.rds"), all_files)]
# read in file containing doublet summary for sample n
doublet_summary <- readRDS(paste0(dir, n, "/", file))
doublet_summary[[1]] %>%
ggplot() +
geom_point(aes(x = X1, y = X2, col = enrichment), size = .8) +
scale_color_viridis_c() +
guides(fill=guide_legend(title="DoubletEnrichment")) +
labs(title = paste0("Simulated Doublet Enrichment", n))
})
gridExtra::grid.arrange(grobs = doublet_enrichment, ncol = 2)

doublet_density <- map(stages, function(n) {
path <- paste0(dir, n, "/")
all_files <- list.files(path)
file <- all_files[grepl(paste0(n, "-Doublet-Summary.rds"), all_files)]
# read in file containing doublet summary for sample n
doublet_summary <- readRDS(paste0(dir, n, "/", file))
doublet_summary[[2]] %>%
ggplot() +
geom_point(aes(x = x, y = y, col = density), size = .1) +
geom_point(data = doublet_summary[[1]], aes(x = X1, y = X2), size = .1,
alpha = .3) +
scale_color_viridis_c() +
guides(fill=guide_legend(title="Simulated Doublet Density")) +
labs(title = "Simulated doublet density overlayed")
})
gridExtra::grid.arrange(grobs = doublet_density, ncol = 2)

With the function addDoubleScores() information on predicted doublets has been added. Now we will filter the putative doublets. ArchR automatically prints the number of cells removed from each sample and the corresponding percentage.
arguments:
- cutEnrich = minimum cutoff for DoubletEnrichment, number of simulated doublets divided by expected number given a random uniform distribution
- cutScore = minimum cutoff for Doublet Score, represents -log10(binomial adjusted p-value) for the DoubletEnrichmentadd
- filterRatio = maximum ratio of predicted doublets to filter based on number of pass-filter cells (A higher filterRatio means that more cells are removed) e.g. 5000 cells
maximum would be filterRatio * 5000 / 100000 = filterRatio * 5000 * 0.05
This way samples with different percentage of doublets will be filtered accordingly.
As can be seen below, in E7.5_rep1 no doublets are filtered. Why might that be?
# in our case we now have 10 251 cells as opposed to 10 661 cells before
# filtering -> 410 cells were removed (3.85%)
proj <- filterDoublets(ArchRProj = proj)
Use annotated cells
Subset cells
What is the overlap between my QC and the processed and annotated data?
# read in processed scATAC-seq data
proc_atac <- readH5AD("jupyter_notebooks/anndata_atac_peak_matrix.h5ad")
proc_rna <- readH5AD("jupyter_notebooks/anndata_rna.h5ad")
print(paste0("The number of genes in the processed data is: ", dim(proc_rna)[[1]]))
## [1] "The number of genes in the processed data is: 32285"
print(paste0("The number of peaks in the processed data is: ", dim(proc_atac)[[1]]))
## [1] "The number of peaks in the processed data is: 180499"
print(paste0("The number of cells in the processed data is: ", dim(proc_atac)[[2]]))
## [1] "The number of cells in the processed data is: 45991"
print(paste0("The number of cells in the ArchR object is: ", dim(proj)[[2]]))
## [1] "The number of cells in the ArchR object is: "
overlap <- proj$cellNames[proj$cellNames %in% colnames(proc_atac)]
print(paste0("Out of the ", dim(proc_atac) [[2]], " cells found in the processed data, ", length(overlap), " are also found in our ArchR object"))
## [1] "Out of the 45991 cells found in the processed data, 45986 are also found in our ArchR object"
filt_proj <- proj[proj$cellNames[proj$cellNames %in% colnames(proc_atac)], ]
print(paste0("The number of remaining cells is: ", dim(filt_proj)[[2]]))
## [1] "The number of remaining cells is: "
filt_proc_rna <- proc_rna[ , filt_proj$cellNames]
filt_proc_atac <- proc_atac[ , filt_proj$cellNames]
gene_scores <- getMatrixFromProject(filt_proj, useMatrix = "GeneScoreMatrix")
#filt_proj <- loadArchRProject(path = "05_newdata_ArchRObject/")
Save the subset ArchR project and proceed with it for now. It might be interesting to revisit and see if I can cluster and identify the cell types de novo at a later stage!
Dimensionality reduction
Because we can have maximally two accessible alleles per cell, the scATAC-seq data is sparse. Therefore, the majority of accessible regions are not transposed, meaning that most loci will have 0 accessible alleles. A zero could mean “non-accessible” or “not sampled”. For many analysis we can use a binarized matix. Imporantly, the 1s have information, BUT the 0s do not!
A PCA would result in high inter-cell similarity at all 0 positions. An alternative approach for dimensionality reduction is a layered dimensionality reduction. First, Latent Semantic Indexing (LSI) is used. LSI is an approach from language processing. Different samples are the “documents” and different regions/peaks are the “words”.
Iterative LSI
- compute term frequency (depth normalization to 10,000 per single cell)
\(TF = \frac{C_{ij}}{F_{j}}\), the term frequency of \(C_{ij}\) which is tiles i in cell j and \(F_{j}\) being the total number of tiles with insertions in cell j.
- Inverse document frequency
- weights features by how often they occur
- more weight to less frequent insertions
\(IDF_{i} =\log {\frac{N}{1 + DF_{i}}}\) with N being the total number of cells in the dataset and \(DF_{i}\) being the total number of cells (documents) in which the tile i is present.
- The term frequency TF is normalized by the inverse document frequncy IDF. You get a \(TF-IDF\) matrix (term frequency-inverse document frequency) which tells us how important a region/peak is to a sample. In other words you transform a binary matrix to a non-binary matrix.
\(TF-IDF = \log{1 + TF{ij}} * IDF_{i} * 10^{4}\)
- SVD identifies the most valuable information across samples. Then we can use these most valuable features to represent the data in a lower dimensional space
- Clusters are identified with Seurat’s Shared Nearest Neighbor clustering
- Sum accessibility across all single cells in each cluster -> log-normalize
- Identify most variable features across the clusters
- repeat with most variable peaks as features
With LSI we can reduce the dimensionality of the sparse insertion matrix to tens or hundreds. Then UMAP or t-SNE can be used to visualize the data
Unlike in scRNA-seq we cannot select the top highly variable features before dimensionality reduction (high noise, low reproducibility). Rather the iterative LSI approach first computes a LSI on the most accessible tiles (this will identify clusters corresponding to the major cell types). Then, ArchR computes the average accessibility across these clusters across all features. Next, the most variable peaks across these clusters are identified. The most highly accessible peaks are the features of a new round of LSI. We can set how many rounds of LSI we want to be peformed.
Using iterative LSI reduces batch effects. If you see some batch effects you could try to add more LSI iterations and start from a lower initial clustering resolution. Also, the number of variable features can be lowered. #
#filt_proj <- addIterativeLSI(ArchRProj = filt_proj, useMatrix = "TileMatrix",
#name = "atac_LSI_25000")
#filt_proj <- addIterativeLSI(ArchRProj = filt_proj, useMatrix = "TileMatrix",
#name = "atac_LSI_50000")
filt_proj <- addIterativeLSI(ArchRProj = filt_proj, useMatrix = "TileMatrix",
name = "atac_LSI_100000")
Clustering
Here we do not need to perform clustering, because we already have celltype annotations.
Visualization
We will add the celltype annotation from the processed data to our ArchR project. Since we already have annotations, we do not need to perform clustering.
#filt_proj <- addUMAP(ArchRProj = filt_proj, reducedDims = "atac_LSI_25000", force = TRUE)
#filt_proj <- addUMAP(ArchRProj = filt_proj, reducedDims = "atac_LSI_50000", force = TRUE)
filt_proj <- addUMAP(ArchRProj = filt_proj, reducedDims = "atac_LSI_100000",
force = TRUE, verbose = FALSE)
Add the celltype annotations to the ArchRobject
df <- colData(filt_proc_atac) %>% as.data.frame() %>% rownames_to_column("cell") #%>%
#filter(cell %in% filt_proj$cellNames)
filt_proj <- addCellColData(
ArchRProj = filt_proj,
data = setNames(as.character(df$celltype.mapped_seurat), as.character(df$cell)),
name = "celltypes",
cells = df$cell
)
# add custom colors
colPalette_celltypes = c('#532C8A',
'#c19f70',
'#f9decf',
'#c9a997',
'#B51D8D',
'#3F84AA',
'#9e6762',
'#354E23',
'#F397C0',
'#ff891c',
'#635547',
'#C72228',
'#f79083',
'#EF4E22',
'#989898',
'#7F6874',
'#8870ad',
'#647a4f',
'#EF5A9D',
'#FBBE92',
'#139992',
'#cc7818',
'#DFCDE4',
'#8EC792',
'#C594BF',
'#C3C388',
'#0F4A9C',
'#FACB12',
'#8DB5CE',
'#1A1A1A',
'#C9EBFB',
'#DABE99',
'#65A83E',
'#005579',
'#CDE088',
'#f7f79e',
'#F6BFCB')
celltypes <- (as.data.frame(getCellColData(filt_proj)) %>% group_by(celltypes) %>%
summarise(n = n()))$celltypes
col <- setNames(colPalette_celltypes, celltypes)
df <- as_data_frame(cbind(getCellColData(filt_proj), getEmbedding(filt_proj)) ) %>%
rename(c(umap1 = atac_LSI_100000.UMAP_Dimension_1, umap2 = atac_LSI_100000.UMAP_Dimension_2))
variables <- c("celltypes", "Sample", "nFrags", "DoubletScore")
plot1 <- map(c("celltypes"), function(n){
ggplot() +
geom_point(aes(x = df %>% pull("umap1"), y = df %>% pull("umap2"),
col = df %>% pull(n)), size = .8) +
guides(col=guide_legend(title=paste0(n))) +
scale_color_manual(values = col)+
xlab("umap1") +
ylab("umpa2") +
labs(title = paste0(n))
})
plots2 <- map(c("nFrags", "DoubletScore"), function(n){
ggplot() +
geom_point(aes(x = df %>% pull("umap1"), y = df %>% pull("umap2"),
col = df %>% pull(n)), size = .8) +
scale_color_viridis_c() +
guides(fill=guide_legend(title=paste0(n))) +
xlab("umap1") +
ylab("umpa2") +
labs(title = paste0(n))
})
plot3 <- map(c("Sample"), function(n){
ggplot() +
geom_point(aes(x = df %>% pull("umap1"), y = df %>% pull("umap2"),
col = df %>% pull(n)), size = .8) +
guides(col=guide_legend(title=paste0(n))) +
xlab("umap1") +
ylab("umpa2") +
labs(title = paste0(n))
})
plot1
## [[1]]

do.call(gridExtra::grid.arrange, c(plot3, plots2, ncol=3))#, nrow = 2))

#saveArchRProject(filt_proj, outputDirectory = "02_filtered_annotated_ArchRobject", load = FALSE)
#saveArchRProject(filt_proj, outputDirectory = "05_newdata_ArchRObject/", load = FALSE)
TODO: Continue here!
Check gene scores for the different celltypes
filt_proj <- addImputeWeights(filt_proj, reducedDims = "atac_LSI_100000")
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
markerGenes <- c(
"Dnmt3b", # Epigblas
"Ptn", # Forebrain/Midbrain/Hindbrain
"Pou5f1", # Primitive Streak
"Myl7", # cardiomyocytes
"Hoxa10" #Allantois
)
#{r, fig.height=10, fig.width=10} p <- plotEmbedding( ArchRProj = filt_proj, colorBy = "GeneScoreMatrix", name = markerGenes, embedding = "UMAP", imputeWeights = getImputeWeights(proj) ) do.call(gridExtra::grid.arrange, c(p, ncol = 3))
Visualizing Genome Browser Tracks
Browse local chromatin accessibility at marker genes. Plot genome browser tracks per celltype
p <- plotBrowserTrack(
ArchRProj = filt_proj,
groupBy = "celltypes",
geneSymbol = markerGenes, # the plot window is centered at the TSS
upstream = 50000,
downstream = 50000
)
- Dnmt3b = Epiblast
- Ptn = Forebrain/Midbrain/Hindbrain
- Pouf5f1 = Primitive Streak
- Myl7 = cardiomyocytes
- Hoxa10 = Allantois
Since we group the cells accoring to celltype below and plot the average signal/accessibility. Since some signals are only expected in a subset of the embryonic stages, the signals might be diluted if we look at all stages at once and take the mean accessibility over different stages. It would probabl
```#{r,fig.height=15, fig.width=10} for (elem in p){ grid::grid.newpage() grid::grid.draw(elem) }
# Different Time points
To differentiate between expression at different stages we will have to split
the ArchR projects into
TODO
# Combine with RNA-seq data
Since we are using multiome data, we do not need to integrate the ATAC and RNA
data, but we have both informations from the same cell. We will use the processed
scRNA-seq data which we filtered to contain only the cells which overlap with
the cells in the ArchR object.
Look here for more details on multiome analysis in ArchR:
https://greenleaflab.github.io/ArchR_2020/Ex-Analyze-Multiome.html
One interesting thing to notice is that the number of genes in the gene score
matrix is different from the number of genes in the RNA dataset.
I get an error when trying to extract the tile matrix. Not sure what is happening
there.
```r
gene_score_mat <- assays(gene_scores)[[1]]
# set names of the gene score matrix
rownames(gene_score_mat) <- rowData(gene_scores)$name
print(paste0("The number of genes in the ATAC-seq Gene Score Matrix is: ",
nrow(gene_score_mat)))
## [1] "The number of genes in the ATAC-seq Gene Score Matrix is: 24333"
print(paste0("The number of genes in the RNA-seq Gene Expression Matrix is: ",
dim(filt_proc_rna)[1]))
## [1] "The number of genes in the RNA-seq Gene Expression Matrix is: 32285"
print(paste0("The number of genes found in both the Gene Expression Matrix and the Gene Score Matrix:",
length(intersect(rownames(gene_score_mat), rownames(filt_proc_rna)))))
## [1] "The number of genes found in both the Gene Expression Matrix and the Gene Score Matrix:22273"
Filter genes which are found in gene score matrix and gene expression matrix. Reorder cell names to match between gene scores and gene expression. Here it would be nice to show VennDiagrams to quickly be able to understand what the differences in genes are between ArchR object and gene expression matrix. Is there a way to include also genes which do not overlap?
# Try to reorder the cellnames to the
# this is the order of the cell names in the ArchR project gene score matrix
order <- rownames(getCellColData(filt_proj))
# extract the gene expression matrix
rna_counts <- assays(filt_proc_rna)[[1]]
# reorder gene expression matrix cell names to match the cellnames of the ArchR object
rna_counts <- rna_counts[, order]
# I checked that they are the same
#colnames(rna_counts) == rownames(getCellColData(filt_proj))
# get the gene names of the gene score matrix
genes <- rownames(gene_score_mat)
# subset the gene expression matrix to obtain only the genes found in the gene
# score matrix
rna_counts <- rna_counts[rownames(rna_counts) %in% genes,]
gene_score_mat <- gene_score_mat[rownames(gene_score_mat) %in% rownames(rna_counts),]
rm(genes)
rm(proc_atac)
rm(proc_rna)
Next we have to create a GRanges object which will be used in the next step to create a RangedSummarizedExperiment. When using the gene annotation metadata stored in the rowData of the RNA-SCE for creating this RangedSummarizedExperiment with the ArchRobject, I got an error message. This might have been due to the fact that the gene annotations looked different between the RNA-SCE and the ArchR object. Therefore, I tried using the same gene annotation as ArchR uses. In order for this to work, we will have to exclude any genes which are not used by ArchR.
# get the Granges object from the Archr project
archr_granges <-getGenes(filt_proj)
# filter for genes which are included in the gene expression matrix
granges <- subset(archr_granges, symbol %in% rownames(rna_counts))
# the problem here was that the gene annotations of the expression matrix
did not match the gene annotations of the Archr object.
df <- rowData(rna_overlap) %>% as.data.frame() %>% filter(gene %in%genes)
df
granges <- df %>% GRanges()
granges
We will now add the gene expression matrix to the ArchR object. First, we have to convert the Single Cell Experiment to a Summarized Experiment.
# create summarized experiment of the scRNA-seq
seRNA <- SummarizedExperiment(assays = list(counts=rna_counts),
colData = colnames(filt_proc_rna),
rowRanges = granges)
seRNA
# add RNA gene expression matrix to the ArchR object
#filt_proj <- addGeneExpressionMatrix(input = filt_proj, seRNA = seRNA, force = TRUE)
# save the ArchR object with the added gene expression matrix
#saveArchRProject(filt_proj, outputDirectory = "08_ArchRobject_all_timepoints_added_gene_expr/", load = FALSE)
We will add a second LSI for the RNA data. The one based on the ATAC data is called “LSI_ATAC”. We will call the one for the RNA data “LSI_RNA”
marker_genes <- c("Lamb1", "Plat", "Sparc", "Elf5", "Ascl2", "Tfap2c", "Ttr",
"Apoa2", "Apoe", "Cystm1", "Emb", "Spink1", "Amot", "Krt19")
p <- plotEmbedding(
ArchRProj = filt_proj,
colorBy = "GeneScoreMatrix",
name = marker_genes,
embedding = "UMAP",
quantCut = c(0.01, 0.95),
imputeWeights = NULL
)
p <- plotEmbedding(
ArchRProj = filt_proj,
colorBy = "GeneScoreMatrix",
name = marker_genes,
embedding = "UMAP",
quantCut = c(0.01, 0.95),
imputeWeights = NULL
)
Visualize the gene activity scores
heatmap(as.matrix(gene_score_mat[1:5, 1:5]))

print(paste0("Minimum gene score: ", min(gene_score_mat)))
## [1] "Minimum gene score: 0"
print(paste0("Maximum gene score: ", max(gene_score_mat)))
## [1] "Maximum gene score: 237.301"
print(paste0("Number of genes in the gene activity score matrix: ", dim(gene_score_mat)[[1]]))
## [1] "Number of genes in the gene activity score matrix: 22273"
tibble(gene = rownames(gene_score_mat),
mean = Matrix::rowMeans(gene_score_mat),
var = MatrixGenerics::rowVars(gene_score_mat)) %>%
mutate(log10_mean = log10(mean),
log10_var = log10(var)) %>% ggplot() +
geom_point(aes(x = log10_mean, y = log10_var)) +
labs(title = "Gene activity score from scATAC-seq")

Visualize the raw gene expression counts
print(paste0("Number of genes in gene expression matrix: ", dim(rna_counts)[[1]]))
## [1] "Number of genes in gene expression matrix: 22273"
tibble(gene = rownames(rna_counts),
mean = Matrix::rowMeans(rna_counts),
var = MatrixGenerics::rowVars(rna_counts)) %>%
mutate(log10_mean = log10(mean),
log10_var = log10(var)) %>% ggplot() +
geom_point(aes(x = log10_mean, y = log10_var)) +
labs(title = "Gene expression raw counts")

Normalize the RNA counts using log2 transformation:
- normalize by library size
- scale with factor 1000
- add pseudocount
- log transform
norm_rna <- Matrix::t(Matrix::t(rna_counts) /
Matrix::colSums(rna_counts))
#scaling factor = 1000
sc_factor <- 1e4
# add pseudocount of 1
lognorm_rna <- (log(norm_rna * sc_factor + 1))
tibble(gene = rownames(lognorm_rna),
mean = Matrix::rowMeans(lognorm_rna),
var = rowVars(lognorm_rna)) %>%
mutate(log10_mean = log10(mean),
log10_var = log10(var)) %>% ggplot() +
geom_point(aes(x = log10_mean, y = log10_var)) +
labs(title = "Gene expression log-normalized RNA expression")
Correlation between gene activity score and gene expression:
marker_genes <- c("Lamb1", "Plat", "Sparc", "Elf5", "Ascl2", "Tfap2c", "Ttr",
"Apoa2", "Apoe", "Cystm1", "Emb", "Spink1", "Amot", "Krt19")
```#{r, fig.width=10, fig.height=15} plots <- map(marker_genes, function(n){ plot <- tibble(gene_expr = lognorm_rna[n, ], gene_score = gene_score_mat[n, ]) %>% ggplot(aes( x = gene_expr,y = gene_score)) + geom_point() + geom_smooth(formula = y ~ x, method = “lm”, size=0.1)
})
gridExtra::grid.arrange(grobs = plots, ncol = 3)
test <- cor(rna_counts[“Apoe”, ], gene_score_mat[“Lamb1”, ]) test
```#{r}
plot <- tibble(gene_expr = lognorm_rna["Lamb1", ],
gene_score = gene_score_mat["Lamb1", ]) %>%
ggplot(aes( x = gene_expr,y = gene_score)) + geom_point() +
geom_smooth(formula = y ~ x, method = "lm", size=0.1)
#ggpubr::stat_cor(aes(label = ..r.label..), method = "pearson", r.digits=2)
plot
test <- ArchR::getMatrixFromProject(filt_proj, useMatrix = "GeneExpressionMatrix")
colData(test) %>% as.data.frame %>% colnames() %>% tail(1) %>% print()
filt_proj <- addIterativeLSI(
ArchRProj = filt_proj,
clusterParams = list(
resolution = 0.2,
sampleCells = 10000,
n.start = 10
),
saveIterations = FALSE,
useMatrix = "GeneExpressionMatrix",
depthCol = "Gex_nUMI",
varFeatures = 2500,
firstSelection = "variable",
binarize = FALSE,
name = "LSI_RNA"
)
ArchR::getCellColData(filt_proj) %>% as.data.frame()
filt_proj <- addIterativeLSI(
ArchRProj = filt_proj,
saveIterations = FALSE,
clusterParams = list(
resolution = 0.2,
sampleCells = 10000,
n.start = 10
),
useMatrix = "GeneExpressionMatrix",
depthCol = "Gex_nUMI",
varFeatures = 100000,
firstSelection = "Var",
binarize = FALSE,
name = "LSI_RNA"
)
Now we will combine the two embeddings.
#Combined Dims
proj <- addCombinedDims(proj, reducedDims = c("LSI_ATAC", "LSI_RNA"), name = "LSI_Combined")
#UMAPs
proj <- addUMAP(proj, reducedDims = "LSI_ATAC", name = "UMAP_ATAC", minDist = 0.8, force = TRUE)
proj <- addUMAP(proj, reducedDims = "LSI_RNA", name = "UMAP_RNA", minDist = 0.8, force = TRUE)
proj <- addUMAP(proj, reducedDims = "LSI_Combined", name = "UMAP_Combined", minDist = 0.8, force = TRUE)
LS0tCnRpdGxlOiAiTW91c2UgR2FzdHJ1bGF0aW9uIHNjQVRBQy1zZXEgJiBzY1JOQS1zZXEiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiA1CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdGhlbWU6IGNvc21vCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlCi0tLQoKPHN0eWxlPgpib2R5IHsKdGV4dC1hbGlnbjoganVzdGlmeX0KPC9zdHlsZT4KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBGQUxTRSwgYXV0b2RlcCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvZ2FzdHJ1bGF0aW9uX2RhdGEvIikKc2V0d2QoIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvZ2FzdHJ1bGF0aW9uX2RhdGEvIikKCnNldC5zZWVkKDEpCmBgYAoKICAKYGBge3J9CiMjIExvYWQgbGlicmFyaWVzCgoKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoQXJjaFIpKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHJoZGY1KQpsaWJyYXJ5KHV3b3QpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNjYXRlcikKbGlicmFyeSh6ZWxsa29udmVydGVyKQpsaWJyYXJ5KGNvcnJwbG90KQojbGlicmFyeShjYXJldCkKaDVkaXNhYmxlRmlsZUxvY2tpbmcoKQpgYGAKICAKICAKVGhlIHNjQVRBQy1zZXEgZGF0YXNldCBjb250YWlucyB0d28gcmVwbGljYXRlcyBlYWNoIGZyb20gNCBzdGFnZXMgb2YgbW91c2UgCmdhc3RydWxhdGlvbiwgRTcuNSwgRTguMCwgRTguNSBhbmQgRTguNzUuIFdlIHdpbGwgdXNlIHRoZSBtbTEwIEdlbm9tZSwgYmVjYXVzZSAKdGhhdCBpcyB0aGUgZ2Vub21lLgoKYGBge3J9CiMgbmFtZXMgb2YgZ2FzdHJ1bGF0aW9uIHN0YWdlcwpzdGFnZXMgPC0gYygiRTcuNV9yZXAxIiwgIkU3LjVfcmVwMiIsICJFOC4wX3JlcDEiLCAiRTguMF9yZXAyIiwgCiAiRTguNV9yZXAxIiwgIkU4LjVfcmVwMiIsICJFOC43NV9yZXAxIiwgIkU4Ljc1X3JlcDIiKQoKIyBjb21iaW5lIGFsbCByYXcgZnJhZ21lbnQgZmlsZXMKaW5wdXRGaWxlcyA8LSBzZXROYW1lcyhjKCJyYXdfZmlsZXMvRTcuNV9yZXAxX2F0YWNfZnJhZ21lbnRzLnRzdi5neiIsCiAgICAgICAgICAgICAgICAgICAgICAgInJhd19maWxlcy9FNy41X3JlcDJfYXRhY19mcmFnbWVudHMudHN2Lmd6IiwKICAgICAgICAgICAgICAgICAgICAgICAicmF3X2ZpbGVzL0U4LjBfcmVwMV9hdGFjX2ZyYWdtZW50cy50c3YuZ3oiLAogICAgICAgICAgICAgICAgICAgICAgICJyYXdfZmlsZXMvRTguMF9yZXAyX2F0YWNfZnJhZ21lbnRzLnRzdi5neiIsCiAgICAgICAgICAgICAgICAgICAgICAgInJhd19maWxlcy9FOC41X3JlcDFfYXRhY19mcmFnbWVudHMudHN2Lmd6IiwKICAgICAgICAgICAgICAgICAgICAgICAicmF3X2ZpbGVzL0U4LjVfcmVwMl9hdGFjX2ZyYWdtZW50cy50c3YuZ3oiLAogICAgICAgICAgICAgICAgICAgICAgICJyYXdfZmlsZXMvRTguNzVfcmVwMV9hdGFjX2ZyYWdtZW50cy50c3YuZ3oiLAogICAgICAgICAgICAgICAgICAgICAgICJyYXdfZmlsZXMvRTguNzVfcmVwMl9hdGFjX2ZyYWdtZW50cy50c3YuZ3oiKSwKICAgICAgICAgICAgICAgICAgICAgICBjKCJFNy41X3JlcDEiLCAiRTcuNV9yZXAyIiwgIkU4LjBfcmVwMSIsICJFOC4wX3JlcDIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICJFOC41X3JlcDEiLCAiRTguNV9yZXAyIiwgIkU4Ljc1X3JlcDEiLCAiRTguNzVfcmVwMiIpKQpgYGAKCgoKCmBgYHtyfQphZGRBcmNoUkdlbm9tZSgibW0xMCIpCmBgYAoKIyMgQ3JlYXRlIEFycm93IEZpbGVzCgpXZSB3aWxsIHJlYWQgaW4gdGhlIGFjY2Vzc2libGUgZnJhZ21lbnRzIGZyb20gdGhlIGZyYWdtZW50cyBmaWxlLiBRQyBpbmZvcm1hdGlvbgppcyBhdXRvbWF0aWNhbGx5IGNvbXB1dGVkIGZvciBlYWNoIGNlbGwuIEJhc2VkIG9uIHZpc3VhbCBpbnNwZWN0aW9uIG9mIFRTUyBFbnJpY2htZW50CnNjb3JlIHZzIGxvZzEwKG5GcmFncykgSSB1c2VkIGEgdGhyZXNob2xkIG9mIGF0IGxlYXN0IDEwMDAgdW5pcXVlIGZyYWdtZW50cyBhbmQKYSBUU1MgRW5yaWNobWVudCBzY29yZSBvZiBhdCBsZWFzdCAyLjc1LiBBdCB0aGUgYmVnaW5uaW5nIGl0IGlzIGJldHRlciB0byBiZSBsZXNzCnN0cmluZ2VudCwgYmVjYXVzZSB3ZSBjYW4gc3RpbGwgZmlsdGVyIHRoZSBkYXRhIGF0IGEgbGF0ZXIgc3RhZ2UuIFRoZSBjZWxscyBhcmUgCmZpbHRlcmVkIGJhc2VkIG9uIHRoZXNlIHRocmVzaG9sZCBhbmQgZGF0YSBhcmUgc3RvcmVkIGluIGFycm93IGZpbGVzLiBHZW5vbWUtd2lkZQp0aWxlIG1hdHJpY2VzIGFyZSBjcmVhdGVkIHVzaW5nIDUwMC1icCBiaW5zLiBUaGUgdGlsZSBtYXRyaXggVCBpcyBiaW5hcnkgbWF0cml4IAphbmQgY29udGFpbnMgaW5mb3JtYXRpb24gd2hldGhlciBpbiBhIHBhcnRpY3VsYXIgNTAwLWJwIGJpbi90aWxlIGkgdGhlcmUgd2VyZSBhbnkgaW5zZXJ0aW9ucyBpbiBhCnBhcnRpY3VsYXIgY2VsbCBqLgoKJFQgXGluIFxtYXRoYmJ7Un1ee05YRH0kIHdpdGggJFRfe2ksan0gPSAxJCBpZiBhbiBpbnNlcnRpb24gaXMgcHJlc2VudCBpbiB0aWxlICRpJAphbmQgY2VsbCAkaiQuICRUX3tpLGp9ID0gMCQgaWYgbm8gaW5zZXJ0aW9uIGlzIHByZXNlbnQgaW4gdGhhdCBjZWxsLCBtZWFuaW5nICRUJCAKaXMgYSBiaW5hcnkgbWF0cml4LgoKSW4gYWRkaXRpb24gYSBHZW5lU2NvcmVNYXRyaXggaXMgY3JlYXRlZC4gCgpJIHNldCB0aGUgc2l6ZSBvZiB0aGUgbWF4aW11bSBudW1iZXIgb2YgZnJhZ21lbnRzIGluY2x1ZGVkIHRvIDFlKzA3LCBpbnN0ZWFkCm9mIHRoZSBkZWZhdWx0IDFlKzA1LCBiZWNhdXNlIHdlIGN1dCBvZmYgc29tZSBjZWxscyBvdGhlcndpc2UuCgpgYGB7cn0KQXJyb3dGaWxlcyA8LSBjcmVhdGVBcnJvd0ZpbGVzKAogIGlucHV0RmlsZXMgPSBpbnB1dEZpbGVzLAogIHNhbXBsZU5hbWVzID0gbmFtZXMoaW5wdXRGaWxlcyksCiAgbWluVFNTID0yLjc1LCAjRG9udCBzZXQgdGhpcyB0b28gaGlnaCBiZWNhdXNlIHlvdSBjYW4gYWx3YXlzIGluY3JlYXNlIGxhdGVyCiAgbWluRnJhZ3MgPSAxMDAwLCAjIG1pbmltdW0gbnVtYmVyIG9mIG1hcHBlZCBmcmFnbWVudHMgcmVxdWlyZWQKICAjIGNvdW50IG1hdHJpeCwgaW5zdGVhZCBvZiB1c2luZyBwZWFrIGl0IHVzZXMgZml4ZWQtd2lkdGggc2xpZGluZyB3aW5kb3cgb2YgYmlucyBhY3Jvc3MgdGhlIHdob2xlIGdlbm9tZQogIGFkZFRpbGVNYXQgPSBUUlVFLCAKICBhZGRHZW5lU2NvcmVNYXQgPSBUUlVFLCAjIHVzZXMgc2lnbmFsIHByb3hpbWFsIHRvIHRoZSBUU1MgdG8gZXN0aW1hdGUgZ2VuZSBhY3Rpdml0eQogIHN1YlRocmVhZGluZyA9IEZBTFNFLAogIG1heEZyYWdzID0gMWUrMDcsCiAgbWluRnJhZ1NpemUgPSAxMCwKICBtYXhGcmFnU2l6ZSA9IDIwMDAsCiAgUUNEaXIgPSAibmV3X0FyY2hSb2JqZWN0L1F1YWxpdHlDb250cm9sIiwKICBmb3JjZSA9IEZBTFNFLCAjIGNoYW5nZSB0byBUUlVFIGlmIHlvdSB3YW50IHRvIG92ZXdyaXRlIHRoZSBmaWxlcwogICMgdGhlIGxlbmd0aCBpbiBicCB0aGF0IHdyYXBzIGFyb3VuZCBudWNsZW9zb21lcyAtPiAKICAgICNpZGVudGlmeSBmcmFnbWVudHMgYXMgc3ViLW51Y2xlb3NvbWUgc3Bhbm5pbmcsIG1vbm8tbnVjbGVvc29tZSBzcGFubmluZyBvciBtdWx0aS1udWNsZW9zb21lIHNwYW5uaW5nCiAgbnVjTGVuZ3RoID0gMTQ3LCAKICAjIGludGVnZXIgdmVjdG9yIC0+IGRlZmluZSByZWdpb24gdXAvZG93bnN0cmVhbSBvZiBUU1MgdG8gaW5jbHVkZSBhcyBwcm9tb3RlciByZWdpb24KICAjIGNhbiBiZSB1c2VkIHRvIGNhbGN1bGF0ZSBlLmcgZnJhY3Rpb24gb2YgcmVhZHMgaW4gcHJvbW90ZXIgKEZJUCkKICBwcm9tb3RlclJlZ2lvbiA9IGMoMjAwMCwgMTAwKSwKICAjIHBhcmFtZXRlcnMgZm9yIGNvbXB1dGluZyBUU1MgZW5yaWNobWVudCBzY29yZXMsIHdpbmRvdyAoYnApIGNlbnRlcmVkIGF0IFRTUyA9IDEwMQogICMgZmxhbmtpbmcgd2luZG93ID0gMjAwMCBicAogICMgbm9ybSA9IHNpemUgb2YgZmxhbmsgd2luZG93IHVzZWQgZm9yIG5vcm1hbGl6YXRpb24gPSAxMDAgYnAKICAjIGFjY2Vzc2liaWxpdHkgd2l0aGluIDEwMSBicCBzdXJyb3VuZGluZyB0aGUgVFNTIHdpbGwgYmUgbm9ybWFsaXplZCB0byBhY2Nlc3NpYmlsaXR5CiAgIyBpbiAxMDAgYnAgYmlucyBmcm9tIC0yMDAwOi0xOTAxYnAgYW5kIDE5MDE6IDIwMDAKICBUU1NQYXJhbXMgPSBsaXN0KDEwMSwgMjAwMCwgMTAwKSwKICAjIHdoaWNoIGNocm9tb3NvbWVzIHRvIGV4Y2x1ZGUgZm9ybSBkb3duc3RyZWFtIGFuYWx5c2lzCiAgIyBpbiBodW1hbiBhbmQgbW91c2U6IG1pdG9jaG9uZHJpYWwgRE5BIChjaHJNKSBhbmQgbWFsZSBzZXggY2hyb21vc29tZSAoY2hyWSkKICAjIHRoZSBmcmFnbWVudHMgYXJlIHN0aWxsIHN0b3JlZCBpbiB0aGUgYXJyb3cgZmlsZXMKICBleGNsdWRlQ2hyID0gYygiY2hyTSIsICJjaHJZIiksCiAgIyBudW1iZXIgb2YgY2h1bmtzIHRvIGRpdmlkZSBjaHJvbW9zb21lcyBpbiAtPiBsb3ctbWVtb3J5IHBhcmFsbGVsaXplZCByZWFkaW5nIG9mIGlucHV0IGZpbGVzCiAgbkNodW5rID0gNSwKICAjIG5hbWUgb2YgZmllbGQgaW4gaW5wdXQgYmFtIGZpbGUgY29udGFpbmluZyB0aGUgYmFyY29kZSB0YWcgaW5mb3JtYXRpb24KICBiY1RhZyA9ICJxbmFtZSIsCiAgb2Zmc2V0UGx1cyA9IDQsICMgb2Zmc2V0IGFwcGxpZWQgdG8gKyBzdHJhbmRlZCBUbjUgaW5zZXJ0aW9uIC0+IGFjY291bnQgZm9yIHByZWNpc2UgVG41IGJpbmRpbmcgc2l0ZQogIG9mZnNldE1pbnVzID0gLTUsIAogIGxvZ0ZpbGUgPSBjcmVhdGVMb2dGaWxlKCJjcmVhdGVBcnJvd3MiKQopCmBgYAoKCgoKIyMgSW5mZXJyaW5nIERvdWJsZXRzCgpTaG91bGQgYmUgcmVtb3ZlZCwgYmVjYXVzZSB0aGV5IGNhbiBpbnRlcmZlcmUgd2l0aCBkb3duc3RyZWFtIGFuYWx5c2lzLgoKKipEb3VibGV0IGRldGVjdGlvbi1hbmQtcmVtb3ZhbCBhbGdvcml0aG06KioKSGV0ZXJvdHlwaWMgZG91YmxldHMgYXJlIGlkZW50aWZpZWQgYnkgZ2VuZXJhdGluZyBhIGNvbGxlY3Rpb24gb2Ygc3ludGhldGljIGRvdWJsZXRzLgpUaGVzZSBzeW50aGV0aWMgZG91YmxldHMgYXJlIHByb2plY3RlZCBpbnRvIGxvdy1kaW1lbnNpb25hbCBlbWJlZGRpbmdzLiBTZWFyY2hpbmcKZm9yIG5lYXJlc3QgbmVpZ2hib3VycyB0byB0aGUgc3ludGhldGljIGRvdWJsZXRzIHdlIGNhbiBpZGVudGlmeSBkb3VibGV0cyBpbiB0aGUKZGF0YXNldC4gVGhpcyBvdXRwZXJmb3JtcyB0aGUgcHJlZGljdGlvbiBvZiBkb3VibGV0cyB1c2luZyBmcmFnbWVudCBudW1iZXIKKFJPQy1BVUMpLiAoQ29tcGFyZWQgdG8gZGVtdXhsZXQgYXMgZ3JvdW5kIHRydXRoKQoKKipXZSBjYW4gYWxzbyBpZGVudGlmeSBkb3VibGV0cyBpbiB0aGUgc2NSTkEtc2VxIHNwYWNlIGlmIHdlIGhhdmUgcGFpcmVkIGRhdGEKYW5kIHJlbW92ZSB0aGUgY2VsbHMgaW4gdGhpcyB3YXkuKioKCmBgYHtyfQpBcnJvd0ZpbGVzIDwtIHNldE5hbWVzKGMoIkU3LjVfcmVwMS5hcnJvdyIsICJFNy41X3JlcDIuYXJyb3ciLCAiRTguMF9yZXAxLmFycm93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICJFOC4wX3JlcDIuYXJyb3ciLCAiRTguNV9yZXAxLmFycm93IiwgIkU4LjVfcmVwMi5hcnJvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiRTguNzVfcmVwMS5hcnJvdyIsICJFOC43NV9yZXAyLmFycm93IiksCiAgICAgICAgICAgICAgICAgICAgICAgYygiRTcuNV9yZXAxIiwgIkU3LjVfcmVwMiIsICJFOC4wX3JlcDEiLCAiRTguMF9yZXAyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAiRTguNV9yZXAxIiwgIkU4LjVfcmVwMiIsICJFOC43NV9yZXAxIiwgIkU4Ljc1X3JlcDIiKSkKYGBgCgoKYGBgI3tyfQojIGZvciBlYWNoIHNhbXBsZSBwcm92aWRlZCBkb3VibGV0IGluZm9ybWF0aW9uIHdpbGwgYmUgYXNzaWduZWQgdG8gZWFjaCBjZWxsCiMgdGhpcyB3YXkgd2UgY2FuIHJlbW92ZSBkb3VibGV0LWJhc2VkIGNsdXN0ZXJzIGRvd25zdHJlYW0KZG91YlNjb3JlcyA8LSBhZGREb3VibGV0U2NvcmVzKAogIHVzZU1hdHJpeCA9ICJUaWxlTWF0cml4IiwKICBpbnB1dCA9IEFycm93RmlsZXMsCiAgayA9IDEwLCAjUmVmZXJzIHRvIGhvdyBtYW55IGNlbGxzIG5lYXIgYSAicHNldWRvLWRvdWJsZXQiIHRvIGNvdW50LgogIG5UcmlhbCA9IDUsICMgbnVtYmVyIG9mIHRpbWUgdG8gc2ltdWxhdGUgbkNlbGwgZG91YmxldHMgCiAga25uTWV0aG9kID0gIlVNQVAiLCAjUmVmZXJzIHRvIHRoZSBkaW1lbnNpb25hbGl0eSByZWR1Y2l0b24gbWV0aG9kIHRvIHVzZSBmb3IgbmVhcmVzdCBuZWlnaGJvciBzZWFyY2guCiAgTFNJTWV0aG9kID0gMSAjIG9kZXIgb2Ygbm9ybWFsaXphdGlvbjogdGYtbG9nKGlkZikKKQpgYGAKCgoKIyMgQ3JlYXRlIEFyY2hSUHJvamVjdAoKQW4gQXJjaFIgUHJvamVjdCBpcyBpbml0aWFsaXplZCB3aXRoIHNvbWUgaW1wb3J0YW50IGF0dHJpYnV0ZXM6CgoqIG91cHV0IGRpcmVjdG9yeQoqIHNhbXBsZSBuYW1lcwoqIGBzYW1wbGVDb2xEYXRhYCAtPiBtYXRyaXggY29udGFpbmluZyBkYXRhIGZvciBlYWNoIHNhbXBsZQoqIGBjZWxsQ29sRGF0YWAgLT4gY29udGFpbnMgZGF0YSBhc3NvY2lhdGVkIHdpdGggZWFjaCBjZWxsCiAgKyBhZnRlciB1c2luZyBgYWRkRG91YmxldFNjb3JlKClgIHRoZXJlIHdpbGwgYmUgYSBjb2x1bW4gCiAgZm9yICJEb3VibGV0IEVucmljaG1lbnQiIGFuZCAiRG91YmxldCBTY29yZSIKKiB0b3RhbCBudW1iZXIgb2YgY2VsbHMgKGV4Y2x1ZGluZyBkb3VibGV0cykKKiBtZWRpYW4gVFNTIHNjb3JlICYgbWVkaWFuIG51bWJlciBvZiBmcmFnbWVudHMgYWNyb3NzIGFsbCBjZWxscyAKYW5kIHNhbXBsZXMKCmBgYCN7cn0KcHJvaiA8LSBBcmNoUlByb2plY3QoCiAgQXJyb3dGaWxlcyA9IEFycm93RmlsZXMsIAogIG91dHB1dERpcmVjdG9yeSA9ICJnYXN0ckFyY2hST3V0cHV0IiwKICBjb3B5QXJyb3dzID0gVFJVRSwgI1RoaXMgaXMgcmVjb21tZW5lZCBzbyB0aGF0IHlvdSBtYWludGFpbiBhbiB1bmFsdGVyZWQgY29weSBmb3IgbGF0ZXIgdXNhZ2UuCiAgZ2VuZUFubm90YXRpb24gPSBnZXRHZW5lQW5ub3RhdGlvbigpLAogICNnZW5vbWVBbm5vdGF0aW9uID0gZ2V0R2VuZUFubm90YXRpb24oKSwKICBzaG93TG9nbyA9IEZBTFNFCikKCnByb2oKYGBgCgpgYGB7cn0KcHJvaiA8LSBsb2FkQXJjaFJQcm9qZWN0KCIwOF9BcmNoUm9iamVjdF9hbGxfdGltZXBvaW50c19hZGRlZF9nZW5lX2V4cHIvIikKYGBgCgoKYGBge3J9CiMgcmVhZCBpbiBleGlzdGluZyBwcm9qZWN0LCBzbyB3ZSBkb24ndCBoYXZlIHRvIHJlcnVuIHRoZSBjcmVhdGlvbgojc2F2ZUFyY2hSUHJvamVjdChwcm9qLCBvdXRwdXREaXJlY3RvcnkgPSAiMDFfRmlyc3RRQ19BcmNoUm9iamVjdC8iLCBsb2FkID0gRkFMU0UpCgojcHJvaiA8LSBsb2FkQXJjaFJQcm9qZWN0KHBhdGggPSAiMDFfRmlyc3RRQ19BcmNoUm9iamVjdC8iKQpgYGAKCgoKIyBRdWFsaXR5IENvbnRyb2wKCkhhdmUgYSBsb29rIGF0IHRoaXMgZm9yIGFkZGl0aW9uYWwgUUMhCmh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9BVEFDc2VxUUMvaW5zdC9kb2MvQVRBQ3NlcVFDLmh0bWwKCjEuIHRoZSBudW1iZXIgb2YgKip1bmlxdWUgbnVjbGVhciBmcmFnbWVudHMqKiAoYXMgb3Bwb3NlZCB0byBtaXRvY2hvbmRyaWFsIGZyYWdtZW50cykKQSBjZWxsIHdpdGggdmVyeSBmZXcgdXNhYmxlIGZyYWdtZW50cyB3aWxsIG5vdCBwcm92aWRlIGVub3VnaCBkYXRhIHRvIG1ha2UgdXNlZnVsIGNvbmNsdXNpb25zLgoyLiAqKnNpZ25hbC10by1iYWNrZ3JvdW5kIHJhdGlvKio6IGlmIHRoaXMgaXMgbG93IHRoaXMgcHJvYmFibHkgY29ycmVzcG9uZHMgdG8gZHlpbmcKY2VsbHMgd2hlcmUgdGhlIGVudGlyZSBnZW5vbWUgYWxsb3dzIHJhbmRvbSB0cmFuc3Bvc2l0aW9uCjMuICoqZnJhZ21lbnQgc2l6ZSBkaXN0cmlidXRpb24qKjogc2luY2UgMTQ3IGJwIGFyZSB3cmFwcGVkIGFyb3VuZCBhIG51Y2xlb3NvbWUgaXQgaXMgCmV4cGVjdGVkIHRoYXQgdGhlcmUgYXJlIGRlcGxldGlvbnMgb2YgZnJhZ21lbnRzIG9mIHRoaXMgbGVuZ3RoIGF0IHJlZ3VsYXIgaW50ZXJ2YWxzLiAKV2UgZXhwZWN0IHRvIHNlZSBhIHBlcmlvZGljIGRpc3RyaWJ1dGlvbiBvZiBmcmFnbWV0biBzaXplIGNvcnJlc3BvbmRpbmcgdG8gbnVjbGVvc29tZXMKKG1vbm8sIGRpLCB0cmksIC4uLiksIGJlY2F1c2UgVG41IGNhbm5vdCBjdXQgRE5BIHRoYXQgaXMgdGlnaHRseSB3cmFwcGVkIGFyb3VuZCAKYSBudWNsZW9zb21lLiAKCkRhdGEgYmVmb3JlIFFDIGFuZCBjb3JyZXNwb25kaW5nIHBsb3RzIGFyZSBzYXZlZCBpbiB0aGUgUXVhbGl0eSBDb250cm9sIG91dHB1dApmb2xkZXIuCgojIyBsb2cxMCh1bmlxdWUgZnJhZ21lbnRzKSB2cyBUU1MgZW5yaWNobWVudAoKIyMjIGJlZm9yZSBRQwoKV2UgdXNlZCBhIFRTUyBlbnJpY2htZW50IHNjb3JlIG9mID4gMi43NSBhbmQgYSBudW1iZXIgb2YgdW5pcXVlIGZyYWdtZW50cyA+IDEwMDAgKGxvZzEwKDEwMDApID0gMykuIApJdCBtaWdodCBtYWtlIHNlbnNlIHRvIHVzZSBkaWZmZXJlbnQgdGhyZXNob2xkcyBmb3IgZGlmZmVyZW50IHNhbXBsZXMuCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpkaXIgPC0gIlF1YWxpdHlDb250cm9sLyIKI2FsbF9maWxlcyA8LSBsaXN0LmZpbGVzKHBhdGgpCgoKYmVmb3JlUUMgPC0gbWFwKHN0YWdlcywgZnVuY3Rpb24obikgewogIHBhdGggPC0gcGFzdGUwKGRpciwgbiwgIi8iKQogIGFsbF9maWxlcyA8LSBsaXN0LmZpbGVzKHBhdGgpCiAgZmlsZSA8LSBhbGxfZmlsZXNbZ3JlcGwocGFzdGUwKG4sICItUHJlLUZpbHRlci1NZXRhZGF0YS5yZHMiKSwgYWxsX2ZpbGVzKV0KICAjIHJlYWQgaW4gZmlsZSBmb3Igc2FtcGxlIG4gY29udGFpbmluZyBvcmlnaW5hbCBkYXRhIGJlZm9yZSBRQwogIGRmIDwtIGFzLmRhdGEuZnJhbWUocmVhZFJEUyhwYXN0ZTAoZGlyLCBuLCAiLyIsIGZpbGUpKSkKICBkZiAlPiUgbXV0YXRlKFNhbXBsZSA9IG4pICU+JSAKICAgIGdncGxvdChhZXMoeCA9IGxvZzEwKG5GcmFncyksIHkgPSBUU1NFbnJpY2htZW50KSkgKwogICAgZ2VvbV9kZW5zaXR5MmRfZmlsbGVkKCkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMi43NSwgY29sb3I9ImdyZWVuIiwgbGluZXR5cGU9ImRhc2hlZCIpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGxvZzEwKDEwMDApLCBjb2xvcj0iZ3JlZW4iLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogICAgI2dlb21feHNpZGVkZW5zaXR5KGFlcyh4PWxvZzEwKHByZV9maWx0ZXJfbWV0YSRuRnJhZ3MpKSkgKwogICAgI2dlb21feXNpZGVkZW5zaXR5KGFlcyh5ID0gcHJlX2ZpbHRlcl9tZXRhJFRTU0VucmljaG1lbnQpKSArCiAgICB5bGltKGMoMi41LCA0LjUpKSArCiAgICBmYWNldF93cmFwKH5TYW1wbGUpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgbGFicyh4ID0gIkxvZzEwIFVuaXF1ZSBGcmFnbWVudHMiLCB5ID0gIlRTUyBFbnJpY2htZW50IFNjb3JlIikKfSkKICAKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSBiZWZvcmVRQywgbmNvbCA9IDMpCgoKCmBgYAoKCgojIyMgYWZ0ZXIgUUMKCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQptZXRhZGF0YSA8LSBhcy5kYXRhLmZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKQptZXRhZGF0YSAlPiUgaGVhZAoKbWV0YWRhdGEgJT4lIGdyb3VwX2J5KFNhbXBsZSkgJT4lIHN1bW1hcmlzZShuID0gbigpKQoKbWV0YWRhdGEgJT4lIGdncGxvdCgpICsKICBnZW9tX2RlbnNpdHkyZF9maWxsZWQoYWVzKHggPSBsb2cxMChuRnJhZ3MpLCB5ID0gVFNTRW5yaWNobWVudCkpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAyLjc1LCBjb2xvcj0iZ3JlZW4iLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGxvZzEwKDEwMDApLCBjb2xvcj0iZ3JlZW4iLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogICNnZW9tX3hzaWRlZGVuc2l0eShhZXMoeD1sb2cxMChwcmVfZmlsdGVyX21ldGEkbkZyYWdzKSkpICsKICAjZ2VvbV95c2lkZWRlbnNpdHkoYWVzKHkgPSBwcmVfZmlsdGVyX21ldGEkVFNTRW5yaWNobWVudCkpICsKICB5bGltKGMoMi41LCA0LjUpKSArCiAgZmFjZXRfd3JhcCh+U2FtcGxlKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgbGFicyh4ID0gIkxvZzEwIFVuaXF1ZSBGcmFnbWVudHMiLCB5ID0gIlRTUyBFbnJpY2htZW50IFNjb3JlIikKCmBgYAoKIyMjIEFyY2hSIHBsb3RzCgpgYGAje3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTE1fQojY3JlYXRlIDMgc2VwYXJhdGUgZGF0YWZyYW1lcyBmb3IgYWxsIHNhbXBsZXMKc2FtcGxlcyA8LSBtYXAodW5pcXVlKHByb2okU2FtcGxlKSwgZnVuY3Rpb24obmFtZSl7CiAgaW5kZXggPC0gQmlvY0dlbmVyaWNzOjp3aGljaChwcm9qJFNhbXBsZSAlaW4lIG5hbWUpCiAgY2VsbHMgPC0gcHJvaiRjZWxsTmFtZXNbaW5kZXhdCiAgc2FtcGxlX3N1YnNldCA8LSBwcm9qW2NlbGxzXQogIGRmIDwtIGdldENlbGxDb2xEYXRhKHNhbXBsZV9zdWJzZXQsIHNlbGVjdCA9IGMoImxvZzEwKG5GcmFncykiLCAiVFNTRW5yaWNobWVudCIpKQogIHAgPC0gZ2dQb2ludCgKICAgIHggPSBkZlssIDFdLCB5ID0gZGZbLCAyXSwgCiAgICBjb2xvckRlbnNpdHkgPSBUUlVFLCAjIHNob3VsZCB0aGUgZGVuc2l0eSBvZiBwb2ludHMgb24gdGhlIHBsb3QgYmUgaW5kaWNhdGVkIGJ5IGNvbG9yPwogICAgY29udGludW91c1NldCA9ICJzYW1iYU5pZ2h0IiwgCiAgICB4bGFiZWwgPSAiTG9nMTAgdW5pcXVlIGZyYWdtZW50cyIsCiAgICB5bGFiZWwgPSAiVFNTIGVucmljaG1lbnQiLAogICAgdGl0bGUgPSBwYXN0ZTAoIlNhbXBsZTogIiwgbmFtZSksCiAgICAjeGxpbSA9IGMobG9nMTAoNTAwKSwgcXVhbnRpbGUoZGZbLDFdLCBwcm9icyA9IDAuOTkpKSwKICAgICN5bGltID0gYygwLCBxdWFudGlsZShkZlssMl0sIHByb2JzID0gMC45OSkpCiAgICApICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMi43NSwgbHR5ID0gImRhc2hlZCIpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDMsIGx0eSA9ICJkYXNoZWQiKQp9KQoKI2RvLmNhbGwoImdyaWQuYXJyYW5nZSIsIGMoc2FtcGxlcywgbmNvbD00KSkKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdyb2JzID0gc2FtcGxlcywgbmNvbCA9IDIpCgoKCgpgYGAKCgoKCiMjIyBQbG90dGluZyBzYW1wbGUgc3RhdGlzdGljcwoKKiB3aGVuIHdlIGhhdmUgZGlzdGluY3Qgc2FtcGxlcywgaXQgY2FuIGJlIGltcG9ydGFudCB0byBjb21wYXJlIHZhcmlvdXMKbWV0cmljIGJldHdlZW4gc2FtcGxlcwoqIHJpZGdlIHBsb3RzICYgdmlvbGluIHBsb3RzIGFyZSB1c2VkIGZvciBncm91cGVkIGRhdGEKCmBgYHtyLGZpZy53aWR0aD0xNX0KCnAxIDwtIGFzX2RhdGFfZnJhbWUoZ2V0Q2VsbENvbERhdGEocHJvaikpICU+JSAKICBtdXRhdGUoU2FtcGxlID0gc3RyX3JlbW92ZShTYW1wbGUsICJzY0FUQUNfIikpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gVFNTRW5yaWNobWVudCwgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSAKCnAyIDwtIGFzX2RhdGFfZnJhbWUoZ2V0Q2VsbENvbERhdGEocHJvaikpICU+JSAKICBtdXRhdGUoU2FtcGxlID0gc3RyX3JlbW92ZShTYW1wbGUsICJzY0FUQUNfIikpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2dyaWRnZXM6Omdlb21fZGVuc2l0eV9yaWRnZXMoYWVzKHggPSBUU1NFbnJpY2htZW50LCB5ID0gU2FtcGxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gU2FtcGxlKSwgYWxwaGEgPSAwLjgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpwMyA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fdmlvbGluKGFlcyh4ID0gU2FtcGxlLCB5ID0gVFNTRW5yaWNobWVudCwgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgZ2VvbV9ib3hwbG90KGFlcyh4ID0gU2FtcGxlLCB5ID0gVFNTRW5yaWNobWVudCxmaWxsID0gU2FtcGxlKSwgYWxwaGEgPSAwLjQpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAiVFNTIEVucmljaG1lbnQiKQoKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMywgcDIsIHAxLCBuY29sID0gMykKYGBgCgoKYGBge3IsZmlnLndpZHRoPTE1fQpwMSA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpLCBsb2cxMF9uRnJhZ3MgPSBsb2cxMChuRnJhZ3MpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IGxvZzEwX25GcmFncywgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KQoKcDIgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIG11dGF0ZShTYW1wbGUgPSBzdHJfcmVtb3ZlKFNhbXBsZSwgInNjQVRBQ18iKSwgbG9nMTBfbkZyYWdzID0gbG9nMTAobkZyYWdzKSkgJT4lIAogIGdncGxvdCgpICsKICBnZ3JpZGdlczo6Z2VvbV9kZW5zaXR5X3JpZGdlcyhhZXMoeCA9IGxvZzEwX25GcmFncywgeSA9IFNhbXBsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAKCgpwMyA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpLCBsb2cxMF9uRnJhZ3MgPSBsb2cxMChuRnJhZ3MpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fdmlvbGluKGFlcyh4ID0gU2FtcGxlLCB5ID0gbG9nMTBfbkZyYWdzLCBmaWxsID0gU2FtcGxlKSwgYWxwaGEgPSAwLjgpICsKICBnZW9tX2JveHBsb3QoYWVzKHggPSBTYW1wbGUsIHkgPSBsb2cxMF9uRnJhZ3MsZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC40KSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAibnVtYmVyIG9mIGZyYWdtZW50cyIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMywgcDIsIHAxLCBuY29sID0gMykKYGBgCgoKIyMjIFBsb3QgRnJhZ21lbnQgU2l6ZSBEaXN0cmlidXRpb24gJiBUU1MgRW5yaWNobWVudCBQcm9maWxlcwoKKiB0aGUgZGlzdHJpYnV0aW9uIG9mIGZyYWdtZW50cyBzaXplIGNhbiBiZSB2ZXJ5IGRpZmZlcmVudCBiZXR3ZWVuIHNhbXBsZXMsCmNlbGwgdHlwZXMgYW5kIGJhdGNoZXMgLT4gdGhlc2UgZGlmZmVyZW5jZXMgZG8gbm90IG5lY2Vzc2FyaWx5IGNvcnJlbGF0ZSB3aXRoIApkaWZmZXJlbmNlcyBpbiBxdWFsaXR5CiogdGhlIGRpcCBpcyB0aGUgZnJhZ21lbnQgc2l6ZSBvZiBhIG51Y2xlb3NvbWUgfjE0N2JwCiogVFNTIGVucmljaG1lbnQgcHJvZmlsZXMKICArIGNsZWFyIHBlYWsgaW4gdGhlIGNlbnRlciAKICArIHNtYWxsZXIgc2hvdWxkZXIgcGVhayByaWdodCBvZiB0aGUgY2VudGVyIGNhdXNlZCBieSB3ZWxsIHBvc2l0aW9uZWQgKzEgbnVjbGVvc29tZQoKYGBgI3tyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD01fQpwMSA8LSBwbG90RnJhZ21lbnRTaXplcyhBcmNoUlByb2ogPSBwcm9qKQpwMiA8LSBwbG90VFNTRW5yaWNobWVudChBcmNoUlByb2ogPSBwcm9qKQpnZ0FsaWduUGxvdHMocDEsIHAyLCB0eXBlID0gInYiKQpgYGAKCiMgRmlsdGVyaW5nIERvdWJsZXRzCgpJbiB0aGUgcGxvdHMgYmVsb3cgdGhlIERvdWJsZXQgRW5yaWNobWVudCByZXByZXNlbnRzIHRoZSBlbnJpY2htZW50IG9mIHNpbXVsYXRlZApkb2J1bGV0cyBuZWFyYnkgZWFjaCBjZWxsLCBjb21wYXJlZCB0byB0aGUgZXhwZWN0ZWQgbnVtYmVyIG9mIGRvdWJsZXRzIGlmIGEgCnVuaWZvcm0gZGlzdHJpdXRpb24gaXMgYXNzdW1lZC4KClRoZSBkb3VibGV0IGRlbnNpdHkgdmlzc3VhbGl6ZXMgd2hlcmUgdGhlIHN5bnRoZXRpYyBkb3VibGV0cyBhcmUgbG9jYXRlZCBhZnRlciAKcHJvamVjdGluZyB0aGVtIGludG8gdGhlIDJEIGVtYmVkZGluZy4gCgpUT0RPOiBDaGFnZSBwYXRoIGFuZCBkaXJlY3RvcnkgdG8gZGlmZmVyZW50IG5hbWUsIGJlY2F1c2UgdGhlIGFyZSBmdW5jdGlvbnMhCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQ9MTAsIHJlc3VsdHM9ICJhc2lzIn0KIyB0aGUgZG91YmxldCBpbmZvcm1hdGlvbiBpcyBzYXZlZCBpbiBhIHNpbXBsZUxpc3RvYmplY3QKCgoKZGlyIDwtICJRdWFsaXR5Q29udHJvbC8iCgoKZG91YmxldF9lbnJpY2htZW50IDwtIG1hcChzdGFnZXMsIGZ1bmN0aW9uKG4pIHsKICBwYXRoIDwtIHBhc3RlMChkaXIsIG4sICIvIikKICBhbGxfZmlsZXMgPC0gbGlzdC5maWxlcyhwYXRoKQogIGZpbGUgPC0gYWxsX2ZpbGVzW2dyZXBsKHBhc3RlMChuLCAiLURvdWJsZXQtU3VtbWFyeS5yZHMiKSwgYWxsX2ZpbGVzKV0KICAjIHJlYWQgaW4gZmlsZSBjb250YWluaW5nIGRvdWJsZXQgc3VtbWFyeSBmb3Igc2FtcGxlIG4gCiAgZG91YmxldF9zdW1tYXJ5IDwtIHJlYWRSRFMocGFzdGUwKGRpciwgbiwgIi8iLCBmaWxlKSkKICBkb3VibGV0X3N1bW1hcnlbWzFdXSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBYMSwgeSA9IFgyLCBjb2wgPSBlbnJpY2htZW50KSwgc2l6ZSA9IC44KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0iRG91YmxldEVucmljaG1lbnQiKSkgKwogIGxhYnModGl0bGUgPSBwYXN0ZTAoIlNpbXVsYXRlZCBEb3VibGV0IEVucmljaG1lbnQiLCBuKSkKfSkKICAKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSBkb3VibGV0X2VucmljaG1lbnQsIG5jb2wgPSAyKQoKCgpkb3VibGV0X2RlbnNpdHkgPC0gbWFwKHN0YWdlcywgZnVuY3Rpb24obikgewogIHBhdGggPC0gcGFzdGUwKGRpciwgbiwgIi8iKQogIGFsbF9maWxlcyA8LSBsaXN0LmZpbGVzKHBhdGgpCiAgZmlsZSA8LSBhbGxfZmlsZXNbZ3JlcGwocGFzdGUwKG4sICItRG91YmxldC1TdW1tYXJ5LnJkcyIpLCBhbGxfZmlsZXMpXQogICMgcmVhZCBpbiBmaWxlIGNvbnRhaW5pbmcgZG91YmxldCBzdW1tYXJ5IGZvciBzYW1wbGUgbiAKICBkb3VibGV0X3N1bW1hcnkgPC0gcmVhZFJEUyhwYXN0ZTAoZGlyLCBuLCAiLyIsIGZpbGUpKQogIGRvdWJsZXRfc3VtbWFyeVtbMl1dICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgsIHkgPSB5LCBjb2wgPSBkZW5zaXR5KSwgc2l6ZSA9IC4xKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZG91YmxldF9zdW1tYXJ5W1sxXV0sIGFlcyh4ID0gWDEsIHkgPSBYMiksIHNpemUgPSAuMSwKICAgICAgICAgICAgIGFscGhhID0gLjMpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJTaW11bGF0ZWQgRG91YmxldCBEZW5zaXR5IikpICsKICBsYWJzKHRpdGxlID0gIlNpbXVsYXRlZCBkb3VibGV0IGRlbnNpdHkgb3ZlcmxheWVkIikKCn0pCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IGRvdWJsZXRfZGVuc2l0eSwgbmNvbCA9IDIpCmBgYAoKV2l0aCB0aGUgZnVuY3Rpb24gYGFkZERvdWJsZVNjb3JlcygpYCBpbmZvcm1hdGlvbiBvbiBwcmVkaWN0ZWQgZG91YmxldHMgaGFzIGJlZW4KYWRkZWQuIE5vdyB3ZSB3aWxsIGZpbHRlciB0aGUgcHV0YXRpdmUgZG91YmxldHMuIEFyY2hSIGF1dG9tYXRpY2FsbHkgcHJpbnRzIHRoZSBudW1iZXIgb2YKY2VsbHMgcmVtb3ZlZCBmcm9tIGVhY2ggc2FtcGxlIGFuZCB0aGUgY29ycmVzcG9uZGluZwpwZXJjZW50YWdlLiAKCioqYXJndW1lbnRzOioqCgoqIGN1dEVucmljaCA9IG1pbmltdW0gY3V0b2ZmIGZvciBEb3VibGV0RW5yaWNobWVudCwgbnVtYmVyIG9mIHNpbXVsYXRlZCAKZG91YmxldHMgZGl2aWRlZCBieSBleHBlY3RlZCBudW1iZXIgZ2l2ZW4gYSByYW5kb20gdW5pZm9ybSBkaXN0cmlidXRpb24KKiBjdXRTY29yZSA9IG1pbmltdW0gY3V0b2ZmIGZvciBEb3VibGV0IFNjb3JlLCByZXByZXNlbnRzIC1sb2cxMChiaW5vbWlhbCBhZGp1c3RlZCBwLXZhbHVlKQpmb3IgdGhlIERvdWJsZXRFbnJpY2htZW50YWRkCiogZmlsdGVyUmF0aW8gPSBtYXhpbXVtIHJhdGlvIG9mIHByZWRpY3RlZCBkb3VibGV0cyB0byBmaWx0ZXIgYmFzZWQgb24gbnVtYmVyIG9mIApwYXNzLWZpbHRlciBjZWxscyAoQSBoaWdoZXIgZmlsdGVyUmF0aW8gbWVhbnMgdGhhdCBtb3JlIGNlbGxzIGFyZSByZW1vdmVkKQplLmcuIDUwMDAgY2VsbHMKCm1heGltdW0gd291bGQgYmUgZmlsdGVyUmF0aW8gKiA1MDAwIC8gMTAwMDAwID0gZmlsdGVyUmF0aW8gKiA1MDAwICogMC4wNQoKKipUaGlzIHdheSBzYW1wbGVzIHdpdGggZGlmZmVyZW50IHBlcmNlbnRhZ2Ugb2YgZG91YmxldHMgd2lsbCBiZSBmaWx0ZXJlZCBhY2NvcmRpbmdseS4qKgoKQXMgY2FuIGJlIHNlZW4gYmVsb3csIGluIEU3LjVfcmVwMSBubyBkb3VibGV0cyBhcmUgZmlsdGVyZWQuIFdoeSBtaWdodCB0aGF0IGJlPwoKYGBgI3tyfQojIGluIG91ciBjYXNlIHdlIG5vdyBoYXZlIDEwIDI1MSBjZWxscyBhcyBvcHBvc2VkIHRvIDEwIDY2MSBjZWxscyBiZWZvcmUKIyBmaWx0ZXJpbmcgLT4gNDEwIGNlbGxzIHdlcmUgcmVtb3ZlZCAoMy44NSUpCnByb2ogPC0gZmlsdGVyRG91YmxldHMoQXJjaFJQcm9qID0gcHJvaikKYGBgCgoKCiMgVXNlIGFubm90YXRlZCBjZWxscwoKIyMgU3Vic2V0IGNlbGxzCgpXaGF0IGlzIHRoZSBvdmVybGFwIGJldHdlZW4gbXkgUUMgYW5kIHRoZSBwcm9jZXNzZWQgYW5kIGFubm90YXRlZCBkYXRhPwoKYGBge3J9CiMgcmVhZCBpbiBwcm9jZXNzZWQgc2NBVEFDLXNlcSBkYXRhCnByb2NfYXRhYyA8LSByZWFkSDVBRCgianVweXRlcl9ub3RlYm9va3MvYW5uZGF0YV9hdGFjX3BlYWtfbWF0cml4Lmg1YWQiKQpwcm9jX3JuYSA8LSByZWFkSDVBRCgianVweXRlcl9ub3RlYm9va3MvYW5uZGF0YV9ybmEuaDVhZCIpCgpwcmludChwYXN0ZTAoIlRoZSBudW1iZXIgb2YgZ2VuZXMgaW4gdGhlIHByb2Nlc3NlZCBkYXRhIGlzOiAiLCBkaW0ocHJvY19ybmEpW1sxXV0pKQpwcmludChwYXN0ZTAoIlRoZSBudW1iZXIgb2YgcGVha3MgaW4gdGhlIHByb2Nlc3NlZCBkYXRhIGlzOiAiLCBkaW0ocHJvY19hdGFjKVtbMV1dKSkKcHJpbnQocGFzdGUwKCJUaGUgbnVtYmVyIG9mIGNlbGxzIGluIHRoZSBwcm9jZXNzZWQgZGF0YSBpczogIiwgZGltKHByb2NfYXRhYylbWzJdXSkpCnByaW50KHBhc3RlMCgiVGhlIG51bWJlciBvZiBjZWxscyBpbiB0aGUgQXJjaFIgb2JqZWN0IGlzOiAiLCBkaW0ocHJvailbWzJdXSkpCgpvdmVybGFwIDwtIHByb2okY2VsbE5hbWVzW3Byb2okY2VsbE5hbWVzICVpbiUgY29sbmFtZXMocHJvY19hdGFjKV0KcHJpbnQocGFzdGUwKCJPdXQgb2YgdGhlICIsIGRpbShwcm9jX2F0YWMpIFtbMl1dLCAiIGNlbGxzIGZvdW5kIGluIHRoZSBwcm9jZXNzZWQgZGF0YSwgIiwgbGVuZ3RoKG92ZXJsYXApLCAiIGFyZSBhbHNvIGZvdW5kIGluIG91ciBBcmNoUiBvYmplY3QiKSkKCmZpbHRfcHJvaiA8LSBwcm9qW3Byb2okY2VsbE5hbWVzW3Byb2okY2VsbE5hbWVzICVpbiUgY29sbmFtZXMocHJvY19hdGFjKV0sIF0KCnByaW50KHBhc3RlMCgiVGhlIG51bWJlciBvZiByZW1haW5pbmcgY2VsbHMgaXM6ICIsIGRpbShmaWx0X3Byb2opW1syXV0pKQoKZmlsdF9wcm9jX3JuYSA8LSBwcm9jX3JuYVsgLCBmaWx0X3Byb2okY2VsbE5hbWVzXQpmaWx0X3Byb2NfYXRhYyA8LSBwcm9jX2F0YWNbICwgZmlsdF9wcm9qJGNlbGxOYW1lc10KCmdlbmVfc2NvcmVzIDwtIGdldE1hdHJpeEZyb21Qcm9qZWN0KGZpbHRfcHJvaiwgdXNlTWF0cml4ID0gIkdlbmVTY29yZU1hdHJpeCIpCmBgYAoKYGBge3J9CiNmaWx0X3Byb2ogPC0gbG9hZEFyY2hSUHJvamVjdChwYXRoID0gIjA1X25ld2RhdGFfQXJjaFJPYmplY3QvIikKYGBgCgoKCgpTYXZlIHRoZSBzdWJzZXQgQXJjaFIgcHJvamVjdCBhbmQgcHJvY2VlZCB3aXRoIGl0IGZvciBub3cuIEl0IG1pZ2h0IGJlIGludGVyZXN0aW5nCnRvIHJldmlzaXQgYW5kIHNlZSBpZiBJIGNhbiBjbHVzdGVyIGFuZCBpZGVudGlmeSB0aGUgY2VsbCB0eXBlcyBkZSBub3ZvIGF0IGEgbGF0ZXIKc3RhZ2UhCgoKIyBEaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gCgoKQmVjYXVzZSB3ZSBjYW4gaGF2ZSBtYXhpbWFsbHkgdHdvIGFjY2Vzc2libGUgYWxsZWxlcyBwZXIgY2VsbCwgdGhlIHNjQVRBQy1zZXEgZGF0YQppcyBzcGFyc2UuIFRoZXJlZm9yZSwgdGhlIG1ham9yaXR5IG9mIGFjY2Vzc2libGUgcmVnaW9ucyBhcmUgbm90IHRyYW5zcG9zZWQsIG1lYW5pbmcgCnRoYXQgbW9zdCBsb2NpIHdpbGwgaGF2ZSAwIGFjY2Vzc2libGUgYWxsZWxlcy4gQSB6ZXJvIGNvdWxkIG1lYW4gIm5vbi1hY2Nlc3NpYmxlIiAKb3IgIm5vdCBzYW1wbGVkIi4gRm9yIG1hbnkgYW5hbHlzaXMgd2UgY2FuIHVzZSBhIGJpbmFyaXplZCBtYXRpeC4gKipJbXBvcmFudGx5LCoqCioqdGhlIDFzIGhhdmUgaW5mb3JtYXRpb24sIEJVVCB0aGUgMHMgZG8gbm90ISoqCgpBIFBDQSB3b3VsZCByZXN1bHQgaW4gaGlnaCBpbnRlci1jZWxsIHNpbWlsYXJpdHkgYXQgYWxsIDAgcG9zaXRpb25zLiBBbiBhbHRlcm5hdGl2ZQphcHByb2FjaCBmb3IgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIGlzIGEgKipsYXllcmVkIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbioqLiBGaXJzdCwKKipMYXRlbnQgU2VtYW50aWMgSW5kZXhpbmcgKExTSSkqKiBpcyB1c2VkLiBMU0kgaXMgYW4gYXBwcm9hY2ggZnJvbSBsYW5ndWFnZQpwcm9jZXNzaW5nLiBEaWZmZXJlbnQgc2FtcGxlcyBhcmUgdGhlICJkb2N1bWVudHMiIGFuZCBkaWZmZXJlbnQgcmVnaW9ucy9wZWFrcyBhcmUKdGhlICJ3b3JkcyIuIAoKIyMgSXRlcmF0aXZlIExTSQoKMS4gY29tcHV0ZSB0ZXJtIGZyZXF1ZW5jeSAoZGVwdGggbm9ybWFsaXphdGlvbiB0byAxMCwwMDAgcGVyIHNpbmdsZSBjZWxsKQoKJFRGID0gXGZyYWN7Q197aWp9fXtGX3tqfX0kLCB0aGUgdGVybSBmcmVxdWVuY3kgb2YgJENfe2lqfSQgd2hpY2ggaXMgdGlsZXMgaSAKaW4gY2VsbCBqIGFuZCAkRl97an0kIGJlaW5nIHRoZSB0b3RhbCBudW1iZXIgb2YgdGlsZXMgd2l0aCBpbnNlcnRpb25zIGluIGNlbGwgai4KCjIuIEludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5IAogICogd2VpZ2h0cyBmZWF0dXJlcyBieSBob3cgb2Z0ZW4gdGhleSBvY2N1ciAKICAqIG1vcmUgd2VpZ2h0IHRvIGxlc3MgZnJlcXVlbnQgaW5zZXJ0aW9ucwoKJElERl97aX0gPVxsb2cge1xmcmFje059ezEgKyBERl97aX19fSQgd2l0aCBOIGJlaW5nIHRoZSB0b3RhbCBudW1iZXIgb2YgY2VsbHMgaW4gdGhlIGRhdGFzZXQgCmFuZCAkREZfe2l9JCBiZWluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGNlbGxzIChkb2N1bWVudHMpIGluIHdoaWNoIHRoZSB0aWxlIGkgaXMgcHJlc2VudC4KCjMuIFRoZSB0ZXJtIGZyZXF1ZW5jeSBURiBpcyBub3JtYWxpemVkIGJ5IHRoZSBpbnZlcnNlIGRvY3VtZW50IGZyZXF1bmN5IElERi4gCllvdSBnZXQgYSAkVEYtSURGJCBtYXRyaXggKHRlcm0gZnJlcXVlbmN5LWludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5KSB3aGljaAp0ZWxscyB1cyBob3cgaW1wb3J0YW50IGEgcmVnaW9uL3BlYWsgaXMgdG8gYSBzYW1wbGUuIEluIG90aGVyIHdvcmRzIHlvdSB0cmFuc2Zvcm0KYSBiaW5hcnkgbWF0cml4IHRvIGEgbm9uLWJpbmFyeSBtYXRyaXguCgokVEYtSURGID0gXGxvZ3sxICsgVEZ7aWp9fSAqIElERl97aX0gKiAxMF57NH0kCgo0LiBTVkQgaWRlbnRpZmllcyB0aGUgbW9zdCB2YWx1YWJsZSBpbmZvcm1hdGlvbiBhY3Jvc3Mgc2FtcGxlcy4gVGhlbiAKd2UgY2FuIHVzZSB0aGVzZSBtb3N0IHZhbHVhYmxlIGZlYXR1cmVzIHRvIHJlcHJlc2VudCB0aGUgZGF0YSBpbiBhIGxvd2VyIGRpbWVuc2lvbmFsIHNwYWNlCjUuIENsdXN0ZXJzIGFyZSBpZGVudGlmaWVkIHdpdGggU2V1cmF0J3MgU2hhcmVkIE5lYXJlc3QgTmVpZ2hib3IgY2x1c3RlcmluZwo2LiBTdW0gYWNjZXNzaWJpbGl0eSBhY3Jvc3MgYWxsIHNpbmdsZSBjZWxscyBpbiBlYWNoIGNsdXN0ZXIgLT4gbG9nLW5vcm1hbGl6ZQo3LiBJZGVudGlmeSBtb3N0IHZhcmlhYmxlIGZlYXR1cmVzIGFjcm9zcyB0aGUgY2x1c3RlcnMKOC4gcmVwZWF0IHdpdGggbW9zdCB2YXJpYWJsZSBwZWFrcyBhcyBmZWF0dXJlcwoKV2l0aCBMU0kgd2UgY2FuIHJlZHVjZSB0aGUgZGltZW5zaW9uYWxpdHkgb2YgdGhlIHNwYXJzZSBpbnNlcnRpb24gbWF0cml4IHRvIHRlbnMgCm9yIGh1bmRyZWRzLiBUaGVuIFVNQVAgb3IgdC1TTkUgY2FuIGJlIHVzZWQgdG8gdmlzdWFsaXplIHRoZSBkYXRhCgoKVW5saWtlIGluIHNjUk5BLXNlcSB3ZSBjYW5ub3Qgc2VsZWN0IHRoZSB0b3AgaGlnaGx5IHZhcmlhYmxlIGZlYXR1cmVzIGJlZm9yZSAKZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIChoaWdoIG5vaXNlLCBsb3cgcmVwcm9kdWNpYmlsaXR5KS4gUmF0aGVyIHRoZSBpdGVyYXRpdmUgCkxTSSBhcHByb2FjaCBmaXJzdCBjb21wdXRlcyBhIExTSSAKb24gdGhlIG1vc3QgYWNjZXNzaWJsZSB0aWxlcyAodGhpcyB3aWxsIGlkZW50aWZ5IGNsdXN0ZXJzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIAptYWpvciBjZWxsIHR5cGVzKS4gVGhlbiwgQXJjaFIgY29tcHV0ZXMgdGhlIGF2ZXJhZ2UgYWNjZXNzaWJpbGl0eSBhY3Jvc3MgdGhlc2UgCmNsdXN0ZXJzIGFjcm9zcyBhbGwgZmVhdHVyZXMuIE5leHQsIHRoZSBtb3N0IHZhcmlhYmxlIHBlYWtzIGFjcm9zcyB0aGVzZSBjbHVzdGVycwphcmUgaWRlbnRpZmllZC4gVGhlIG1vc3QgaGlnaGx5IGFjY2Vzc2libGUgcGVha3MgYXJlIHRoZSBmZWF0dXJlcyBvZiBhIG5ldyAKcm91bmQgb2YgTFNJLiBXZSBjYW4gc2V0IGhvdyBtYW55IHJvdW5kcyBvZiBMU0kgd2Ugd2FudCB0byBiZSBwZWZvcm1lZC4gCgpVc2luZyBpdGVyYXRpdmUgTFNJIHJlZHVjZXMgYmF0Y2ggZWZmZWN0cy4gSWYgeW91IHNlZSBzb21lIGJhdGNoIGVmZmVjdHMgeW91IGNvdWxkCnRyeSB0byBhZGQgbW9yZSBMU0kgaXRlcmF0aW9ucyBhbmQgc3RhcnQgZnJvbSBhIGxvd2VyIGluaXRpYWwgY2x1c3RlcmluZyAKcmVzb2x1dGlvbi4gQWxzbywgdGhlIG51bWJlciBvZiB2YXJpYWJsZSBmZWF0dXJlcyBjYW4gYmUgbG93ZXJlZC4gICMKCgpgYGAje3J9CiNmaWx0X3Byb2ogPC0gYWRkSXRlcmF0aXZlTFNJKEFyY2hSUHJvaiA9IGZpbHRfcHJvaiwgdXNlTWF0cml4ID0gIlRpbGVNYXRyaXgiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjbmFtZSA9ICJhdGFjX0xTSV8yNTAwMCIpCiNmaWx0X3Byb2ogPC0gYWRkSXRlcmF0aXZlTFNJKEFyY2hSUHJvaiA9IGZpbHRfcHJvaiwgdXNlTWF0cml4ID0gIlRpbGVNYXRyaXgiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjbmFtZSA9ICJhdGFjX0xTSV81MDAwMCIpCmZpbHRfcHJvaiA8LSBhZGRJdGVyYXRpdmVMU0koQXJjaFJQcm9qID0gZmlsdF9wcm9qLCB1c2VNYXRyaXggPSAiVGlsZU1hdHJpeCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJhdGFjX0xTSV8xMDAwMDAiKQoKYGBgCgoKIyMgQ2x1c3RlcmluZwoKKipIZXJlIHdlIGRvIG5vdCBuZWVkIHRvIHBlcmZvcm0gY2x1c3RlcmluZywgYmVjYXVzZSB3ZSBhbHJlYWR5IGhhdmUgY2VsbHR5cGUKYW5ub3RhdGlvbnMuKioKCgoKIyBWaXN1YWxpemF0aW9uCgpXZSB3aWxsIGFkZCB0aGUgY2VsbHR5cGUgYW5ub3RhdGlvbiBmcm9tIHRoZSBwcm9jZXNzZWQgZGF0YSB0byBvdXIgQXJjaFIgCnByb2plY3QuIFNpbmNlIHdlIGFscmVhZHkgaGF2ZSBhbm5vdGF0aW9ucywgd2UgZG8gbm90IG5lZWQgdG8gcGVyZm9ybSBjbHVzdGVyaW5nLgoKYGBgI3tyfQojZmlsdF9wcm9qIDwtIGFkZFVNQVAoQXJjaFJQcm9qID0gZmlsdF9wcm9qLCByZWR1Y2VkRGltcyA9ICJhdGFjX0xTSV8yNTAwMCIsIGZvcmNlID0gVFJVRSkKCiNmaWx0X3Byb2ogPC0gYWRkVU1BUChBcmNoUlByb2ogPSBmaWx0X3Byb2osIHJlZHVjZWREaW1zID0gImF0YWNfTFNJXzUwMDAwIiwgZm9yY2UgPSBUUlVFKQoKZmlsdF9wcm9qIDwtIGFkZFVNQVAoQXJjaFJQcm9qID0gZmlsdF9wcm9qLCByZWR1Y2VkRGltcyA9ICJhdGFjX0xTSV8xMDAwMDAiLCAKICAgICAgICAgICAgICAgICAgICAgZm9yY2UgPSBUUlVFLCB2ZXJib3NlID0gRkFMU0UpCgpgYGAKCkFkZCB0aGUgY2VsbHR5cGUgYW5ub3RhdGlvbnMgdG8gdGhlIEFyY2hSb2JqZWN0CgpgYGAje3J9CmRmIDwtIGNvbERhdGEoZmlsdF9wcm9jX2F0YWMpICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpICMlPiUKICAjZmlsdGVyKGNlbGwgJWluJSBmaWx0X3Byb2okY2VsbE5hbWVzKQoKZmlsdF9wcm9qIDwtIGFkZENlbGxDb2xEYXRhKAogIEFyY2hSUHJvaiA9IGZpbHRfcHJvaiwKICBkYXRhID0gc2V0TmFtZXMoYXMuY2hhcmFjdGVyKGRmJGNlbGx0eXBlLm1hcHBlZF9zZXVyYXQpLCBhcy5jaGFyYWN0ZXIoZGYkY2VsbCkpLAogIG5hbWUgPSAiY2VsbHR5cGVzIiwKICBjZWxscyA9IGRmJGNlbGwKKQpgYGAKCgoKYGBge3J9CiMgYWRkIGN1c3RvbSBjb2xvcnMKCgpjb2xQYWxldHRlX2NlbGx0eXBlcyA9IGMoJyM1MzJDOEEnLAogJyNjMTlmNzAnLAogJyNmOWRlY2YnLAogJyNjOWE5OTcnLAogJyNCNTFEOEQnLAogJyMzRjg0QUEnLAogJyM5ZTY3NjInLAogJyMzNTRFMjMnLAogJyNGMzk3QzAnLAogJyNmZjg5MWMnLAogJyM2MzU1NDcnLAogJyNDNzIyMjgnLAogJyNmNzkwODMnLAogJyNFRjRFMjInLAogJyM5ODk4OTgnLAogJyM3RjY4NzQnLAogJyM4ODcwYWQnLAogJyM2NDdhNGYnLAogJyNFRjVBOUQnLAogJyNGQkJFOTInLAogJyMxMzk5OTInLAogJyNjYzc4MTgnLAogJyNERkNERTQnLAogJyM4RUM3OTInLAogJyNDNTk0QkYnLAogJyNDM0MzODgnLAogJyMwRjRBOUMnLAogJyNGQUNCMTInLAogJyM4REI1Q0UnLAogJyMxQTFBMUEnLAogJyNDOUVCRkInLAogJyNEQUJFOTknLAogJyM2NUE4M0UnLAogJyMwMDU1NzknLAogJyNDREUwODgnLAogJyNmN2Y3OWUnLAogJyNGNkJGQ0InKQoKY2VsbHR5cGVzIDwtIChhcy5kYXRhLmZyYW1lKGdldENlbGxDb2xEYXRhKGZpbHRfcHJvaikpICU+JSBncm91cF9ieShjZWxsdHlwZXMpICU+JSAKIHN1bW1hcmlzZShuID0gbigpKSkkY2VsbHR5cGVzCgpjb2wgPC0gc2V0TmFtZXMoY29sUGFsZXR0ZV9jZWxsdHlwZXMsIGNlbGx0eXBlcykKYGBgCgoKYGBge3IsIGZpZy53aWR0aD0xMH0KZGYgPC0gYXNfZGF0YV9mcmFtZShjYmluZChnZXRDZWxsQ29sRGF0YShmaWx0X3Byb2opLCBnZXRFbWJlZGRpbmcoZmlsdF9wcm9qKSkgKSAlPiUKICByZW5hbWUoYyh1bWFwMSA9IGF0YWNfTFNJXzEwMDAwMC5VTUFQX0RpbWVuc2lvbl8xLCB1bWFwMiAgPSBhdGFjX0xTSV8xMDAwMDAuVU1BUF9EaW1lbnNpb25fMikpCgp2YXJpYWJsZXMgPC0gYygiY2VsbHR5cGVzIiwgIlNhbXBsZSIsICJuRnJhZ3MiLCAiRG91YmxldFNjb3JlIikKCgpwbG90MSA8LSBtYXAoYygiY2VsbHR5cGVzIiksIGZ1bmN0aW9uKG4pewogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gZGYgJT4lIHB1bGwoInVtYXAxIiksIHkgPSBkZiAlPiUgcHVsbCgidW1hcDIiKSwgCiAgICAgICAgICAgICAgICAgY29sID0gZGYgJT4lIHB1bGwobikpLCAgc2l6ZSA9IC44KSArCiAgICBndWlkZXMoY29sPWd1aWRlX2xlZ2VuZCh0aXRsZT1wYXN0ZTAobikpKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29sKSsKICAgIHhsYWIoInVtYXAxIikgKwogICAgeWxhYigidW1wYTIiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKG4pKQp9KQoKcGxvdHMyIDwtIG1hcChjKCJuRnJhZ3MiLCAiRG91YmxldFNjb3JlIiksIGZ1bmN0aW9uKG4pewogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gZGYgJT4lIHB1bGwoInVtYXAxIiksIHkgPSBkZiAlPiUgcHVsbCgidW1hcDIiKSwgCiAgICAgICAgICAgICAgICAgY29sID0gZGYgJT4lIHB1bGwobikpLCBzaXplID0gLjgpICsKICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICAgIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT1wYXN0ZTAobikpKSArCiAgICB4bGFiKCJ1bWFwMSIpICsKICAgIHlsYWIoInVtcGEyIikgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlMChuKSkKfSkKCnBsb3QzIDwtIG1hcChjKCJTYW1wbGUiKSwgZnVuY3Rpb24obil7CiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkZiAlPiUgcHVsbCgidW1hcDEiKSwgeSA9IGRmICU+JSBwdWxsKCJ1bWFwMiIpLCAKICAgICAgICAgICAgICAgICBjb2wgPSBkZiAlPiUgcHVsbChuKSksICBzaXplID0gLjgpICsKICAgIGd1aWRlcyhjb2w9Z3VpZGVfbGVnZW5kKHRpdGxlPXBhc3RlMChuKSkpICsKICAgIHhsYWIoInVtYXAxIikgKwogICAgeWxhYigidW1wYTIiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKG4pKQp9KQoKCnBsb3QxCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9OH0KZG8uY2FsbChncmlkRXh0cmE6OmdyaWQuYXJyYW5nZSwgYyhwbG90MywgcGxvdHMyLCBuY29sPTMpKSMsIG5yb3cgPSAyKSkKCmBgYAoKCgpgYGB7cn0KI3NhdmVBcmNoUlByb2plY3QoZmlsdF9wcm9qLCBvdXRwdXREaXJlY3RvcnkgPSAiMDJfZmlsdGVyZWRfYW5ub3RhdGVkX0FyY2hSb2JqZWN0IiwgbG9hZCA9IEZBTFNFKQojc2F2ZUFyY2hSUHJvamVjdChmaWx0X3Byb2osIG91dHB1dERpcmVjdG9yeSA9ICIwNV9uZXdkYXRhX0FyY2hST2JqZWN0LyIsIGxvYWQgPSBGQUxTRSkKCmBgYAoKClRPRE86IENvbnRpbnVlIGhlcmUhCgoKIyBDaGVjayBnZW5lIHNjb3JlcyBmb3IgdGhlIGRpZmZlcmVudCBjZWxsdHlwZXMKCgoKYGBge3J9CmZpbHRfcHJvaiA8LSBhZGRJbXB1dGVXZWlnaHRzKGZpbHRfcHJvaiwgcmVkdWNlZERpbXMgPSAiYXRhY19MU0lfMTAwMDAwIikKYGBgCgpgYGAje3J9Cm1hcmtlckdlbmVzICA8LSBjKAogICAgIkRubXQzYiIsICAjIEVwaWdibGFzCiAgICAiUHRuIiwgIyBGb3JlYnJhaW4vTWlkYnJhaW4vSGluZGJyYWluCiAgICAiUG91NWYxIiwgIyBQcmltaXRpdmUgU3RyZWFrCiAgICAiTXlsNyIsICMgY2FyZGlvbXlvY3l0ZXMKICAgICJIb3hhMTAiICNBbGxhbnRvaXMKCiAgKQpgYGAKCmBgYCN7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQpwIDwtIHBsb3RFbWJlZGRpbmcoCiAgICBBcmNoUlByb2ogPSBmaWx0X3Byb2osIAogICAgY29sb3JCeSA9ICJHZW5lU2NvcmVNYXRyaXgiLCAKICAgIG5hbWUgPSBtYXJrZXJHZW5lcywgCiAgICBlbWJlZGRpbmcgPSAiVU1BUCIsCiAgICBpbXB1dGVXZWlnaHRzID0gZ2V0SW1wdXRlV2VpZ2h0cyhwcm9qKQopCmRvLmNhbGwoZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UsIGMocCwgbmNvbCA9IDMpKQpgYGAKCgoKIyBWaXN1YWxpemluZyBHZW5vbWUgQnJvd3NlciBUcmFja3MKCkJyb3dzZSBsb2NhbCBjaHJvbWF0aW4gYWNjZXNzaWJpbGl0eSBhdCBtYXJrZXIgZ2VuZXMuIFBsb3QgZ2Vub21lCmJyb3dzZXIgdHJhY2tzIHBlciBjZWxsdHlwZSAKCmBgYCN7cn0KcCA8LSBwbG90QnJvd3NlclRyYWNrKAogICAgQXJjaFJQcm9qID0gZmlsdF9wcm9qLCAKICAgIGdyb3VwQnkgPSAiY2VsbHR5cGVzIiwgCiAgICBnZW5lU3ltYm9sID0gbWFya2VyR2VuZXMsICMgdGhlIHBsb3Qgd2luZG93IGlzIGNlbnRlcmVkIGF0IHRoZSBUU1MKICAgIHVwc3RyZWFtID0gNTAwMDAsCiAgIAogICAgIGRvd25zdHJlYW0gPSA1MDAwMAopCgpgYGAKCgoKICAgIAoqIERubXQzYiA9IEVwaWJsYXN0IAoqIFB0biA9IEZvcmVicmFpbi9NaWRicmFpbi9IaW5kYnJhaW4KKiBQb3VmNWYxID0gUHJpbWl0aXZlIFN0cmVhawoqIE15bDcgPSBjYXJkaW9teW9jeXRlcwoqIEhveGExMCA9IEFsbGFudG9pcwoKU2luY2Ugd2UgZ3JvdXAgdGhlIGNlbGxzIGFjY29yaW5nIHRvIGNlbGx0eXBlIGJlbG93IGFuZCBwbG90IHRoZSBhdmVyYWdlCnNpZ25hbC9hY2Nlc3NpYmlsaXR5LiBTaW5jZSBzb21lIHNpZ25hbHMgYXJlIG9ubHkgZXhwZWN0ZWQgaW4gYSBzdWJzZXQgb2YgdGhlCmVtYnJ5b25pYyBzdGFnZXMsIHRoZSBzaWduYWxzIG1pZ2h0IGJlIGRpbHV0ZWQgaWYgd2UgbG9vayBhdCBhbGwgc3RhZ2VzIGF0IG9uY2UKYW5kIHRha2UgdGhlIG1lYW4gYWNjZXNzaWJpbGl0eSBvdmVyIGRpZmZlcmVudCBzdGFnZXMuIEl0IHdvdWxkIHByb2JhYmwKCmBgYCN7cixmaWcuaGVpZ2h0PTE1LCBmaWcud2lkdGg9MTB9CmZvciAoZWxlbSBpbiBwKXsKICBncmlkOjpncmlkLm5ld3BhZ2UoKQogIGdyaWQ6OmdyaWQuZHJhdyhlbGVtKQp9CgpgYGAKCgoKIyBEaWZmZXJlbnQgVGltZSBwb2ludHMKClRvIGRpZmZlcmVudGlhdGUgYmV0d2VlbiBleHByZXNzaW9uIGF0IGRpZmZlcmVudCBzdGFnZXMgd2Ugd2lsbCBoYXZlIHRvIHNwbGl0CnRoZSBBcmNoUiBwcm9qZWN0cyBpbnRvIAoKVE9ETwoKIyBDb21iaW5lIHdpdGggUk5BLXNlcSBkYXRhCgpTaW5jZSB3ZSBhcmUgdXNpbmcgbXVsdGlvbWUgZGF0YSwgd2UgZG8gbm90IG5lZWQgdG8gaW50ZWdyYXRlIHRoZSBBVEFDIGFuZCBSTkEKZGF0YSwgYnV0IHdlIGhhdmUgYm90aCBpbmZvcm1hdGlvbnMgZnJvbSB0aGUgc2FtZSBjZWxsLiBXZSB3aWxsIHVzZSB0aGUgcHJvY2Vzc2VkCnNjUk5BLXNlcSBkYXRhIHdoaWNoIHdlIGZpbHRlcmVkIHRvIGNvbnRhaW4gb25seSB0aGUgY2VsbHMgd2hpY2ggb3ZlcmxhcCB3aXRoIAp0aGUgY2VsbHMgaW4gdGhlIEFyY2hSIG9iamVjdC4KCkxvb2sgaGVyZSBmb3IgbW9yZSBkZXRhaWxzIG9uIG11bHRpb21lIGFuYWx5c2lzIGluIEFyY2hSOgpodHRwczovL2dyZWVubGVhZmxhYi5naXRodWIuaW8vQXJjaFJfMjAyMC9FeC1BbmFseXplLU11bHRpb21lLmh0bWwKCk9uZSBpbnRlcmVzdGluZyB0aGluZyB0byBub3RpY2UgaXMgdGhhdCB0aGUgbnVtYmVyIG9mIGdlbmVzIGluIHRoZSBnZW5lIHNjb3JlIAptYXRyaXggaXMgZGlmZmVyZW50IGZyb20gdGhlIG51bWJlciBvZiBnZW5lcyBpbiB0aGUgUk5BIGRhdGFzZXQuCgpJIGdldCBhbiBlcnJvciB3aGVuIHRyeWluZyB0byBleHRyYWN0IHRoZSB0aWxlIG1hdHJpeC4gTm90IHN1cmUgd2hhdCBpcyBoYXBwZW5pbmcKdGhlcmUuIAoKYGBge3J9CmdlbmVfc2NvcmVfbWF0IDwtIGFzc2F5cyhnZW5lX3Njb3JlcylbWzFdXQoKIyBzZXQgbmFtZXMgb2YgdGhlIGdlbmUgc2NvcmUgbWF0cml4CnJvd25hbWVzKGdlbmVfc2NvcmVfbWF0KSA8LSByb3dEYXRhKGdlbmVfc2NvcmVzKSRuYW1lCmBgYAoKCgpgYGB7cn0KcHJpbnQocGFzdGUwKCJUaGUgbnVtYmVyIG9mIGdlbmVzIGluIHRoZSBBVEFDLXNlcSBHZW5lIFNjb3JlIE1hdHJpeCBpczogIiwKICAgICAgICAgICAgIG5yb3coZ2VuZV9zY29yZV9tYXQpKSkKCnByaW50KHBhc3RlMCgiVGhlIG51bWJlciBvZiBnZW5lcyBpbiB0aGUgUk5BLXNlcSBHZW5lIEV4cHJlc3Npb24gTWF0cml4IGlzOiAiLAogICAgICAgICAgICAgZGltKGZpbHRfcHJvY19ybmEpWzFdKSkKCnByaW50KHBhc3RlMCgiVGhlIG51bWJlciBvZiBnZW5lcyBmb3VuZCBpbiBib3RoIHRoZSBHZW5lIEV4cHJlc3Npb24gTWF0cml4IGFuZCB0aGUgR2VuZSBTY29yZSBNYXRyaXg6IiwgCiAgICAgIGxlbmd0aChpbnRlcnNlY3Qocm93bmFtZXMoZ2VuZV9zY29yZV9tYXQpLCByb3duYW1lcyhmaWx0X3Byb2Nfcm5hKSkpKSkKYGBgCgoKRmlsdGVyIGdlbmVzIHdoaWNoIGFyZSBmb3VuZCBpbiBnZW5lIHNjb3JlIG1hdHJpeCBhbmQgZ2VuZSBleHByZXNzaW9uIG1hdHJpeC4gUmVvcmRlcgpjZWxsIG5hbWVzIHRvIG1hdGNoIGJldHdlZW4gZ2VuZSBzY29yZXMgYW5kIGdlbmUgZXhwcmVzc2lvbi4gSGVyZSBpdCB3b3VsZApiZSBuaWNlIHRvIHNob3cgVmVubkRpYWdyYW1zIHRvIHF1aWNrbHkgYmUgYWJsZSB0byB1bmRlcnN0YW5kIHdoYXQgdGhlIGRpZmZlcmVuY2VzCmluIGdlbmVzIGFyZSBiZXR3ZWVuIEFyY2hSIG9iamVjdCBhbmQgZ2VuZSBleHByZXNzaW9uIG1hdHJpeC4gSXMgdGhlcmUgYSB3YXkKdG8gaW5jbHVkZSBhbHNvIGdlbmVzIHdoaWNoIGRvIG5vdCBvdmVybGFwPwoKYGBge3J9CiMgVHJ5IHRvIHJlb3JkZXIgdGhlIGNlbGxuYW1lcyB0byB0aGUgCgojIHRoaXMgaXMgdGhlIG9yZGVyIG9mIHRoZSBjZWxsIG5hbWVzIGluIHRoZSBBcmNoUiBwcm9qZWN0IGdlbmUgc2NvcmUgbWF0cml4Cm9yZGVyIDwtIHJvd25hbWVzKGdldENlbGxDb2xEYXRhKGZpbHRfcHJvaikpCgojIGV4dHJhY3QgdGhlIGdlbmUgZXhwcmVzc2lvbiBtYXRyaXggCnJuYV9jb3VudHMgPC0gYXNzYXlzKGZpbHRfcHJvY19ybmEpW1sxXV0KCiMgcmVvcmRlciBnZW5lIGV4cHJlc3Npb24gbWF0cml4IGNlbGwgbmFtZXMgdG8gbWF0Y2ggdGhlIGNlbGxuYW1lcyBvZiB0aGUgQXJjaFIgb2JqZWN0CnJuYV9jb3VudHMgPC0gcm5hX2NvdW50c1ssIG9yZGVyXQoKIyBJIGNoZWNrZWQgdGhhdCB0aGV5IGFyZSB0aGUgc2FtZQojY29sbmFtZXMocm5hX2NvdW50cykgPT0gcm93bmFtZXMoZ2V0Q2VsbENvbERhdGEoZmlsdF9wcm9qKSkKCiMgZ2V0IHRoZSBnZW5lIG5hbWVzIG9mIHRoZSBnZW5lIHNjb3JlIG1hdHJpeApnZW5lcyA8LSByb3duYW1lcyhnZW5lX3Njb3JlX21hdCkKCiMgc3Vic2V0IHRoZSBnZW5lIGV4cHJlc3Npb24gbWF0cml4IHRvIG9idGFpbiBvbmx5IHRoZSBnZW5lcyBmb3VuZCBpbiB0aGUgZ2VuZSAKIyBzY29yZSBtYXRyaXgKcm5hX2NvdW50cyA8LSBybmFfY291bnRzW3Jvd25hbWVzKHJuYV9jb3VudHMpICVpbiUgZ2VuZXMsXQpnZW5lX3Njb3JlX21hdCA8LSBnZW5lX3Njb3JlX21hdFtyb3duYW1lcyhnZW5lX3Njb3JlX21hdCkgJWluJSByb3duYW1lcyhybmFfY291bnRzKSxdCgoKcm0oZ2VuZXMpCnJtKHByb2NfYXRhYykKcm0ocHJvY19ybmEpCmBgYAoKTmV4dCB3ZSBoYXZlIHRvIGNyZWF0ZSBhIEdSYW5nZXMgb2JqZWN0IHdoaWNoIHdpbGwgYmUgdXNlZCBpbiB0aGUgbmV4dCBzdGVwIHRvIApjcmVhdGUgYSBSYW5nZWRTdW1tYXJpemVkRXhwZXJpbWVudC4gV2hlbiB1c2luZyB0aGUgZ2VuZSBhbm5vdGF0aW9uIG1ldGFkYXRhCnN0b3JlZCBpbiB0aGUgcm93RGF0YSBvZiB0aGUgUk5BLVNDRSBmb3IgY3JlYXRpbmcgdGhpcyBSYW5nZWRTdW1tYXJpemVkRXhwZXJpbWVudAp3aXRoIHRoZSBBcmNoUm9iamVjdCwgSSBnb3QgYW4gZXJyb3IgbWVzc2FnZS4gVGhpcyBtaWdodCBoYXZlIGJlZW4gZHVlIHRvIHRoZQpmYWN0IHRoYXQgdGhlIGdlbmUgYW5ub3RhdGlvbnMgbG9va2VkIGRpZmZlcmVudCBiZXR3ZWVuIHRoZSBSTkEtU0NFIGFuZCB0aGUgQXJjaFIKb2JqZWN0LiBUaGVyZWZvcmUsIEkgdHJpZWQgdXNpbmcgdGhlIHNhbWUgZ2VuZSBhbm5vdGF0aW9uIGFzIEFyY2hSIHVzZXMuIEluIG9yZGVyIApmb3IgdGhpcyB0byB3b3JrLCB3ZSB3aWxsIGhhdmUgdG8gZXhjbHVkZSBhbnkgZ2VuZXMgd2hpY2ggYXJlIG5vdCB1c2VkIGJ5IEFyY2hSLgoKYGBgI3tyfQojIGdldCB0aGUgR3JhbmdlcyBvYmplY3QgZnJvbSB0aGUgQXJjaHIgcHJvamVjdAphcmNocl9ncmFuZ2VzIDwtZ2V0R2VuZXMoZmlsdF9wcm9qKQoKIyBmaWx0ZXIgZm9yIGdlbmVzIHdoaWNoIGFyZSBpbmNsdWRlZCBpbiB0aGUgZ2VuZSBleHByZXNzaW9uIG1hdHJpeApncmFuZ2VzIDwtIHN1YnNldChhcmNocl9ncmFuZ2VzLCBzeW1ib2wgJWluJSByb3duYW1lcyhybmFfY291bnRzKSkKYGBgCgoKYGBgI3tyfQoKIyB0aGUgcHJvYmxlbSBoZXJlIHdhcyB0aGF0IHRoZSBnZW5lIGFubm90YXRpb25zIG9mIHRoZSBleHByZXNzaW9uIG1hdHJpeApkaWQgbm90IG1hdGNoIHRoZSBnZW5lIGFubm90YXRpb25zIG9mIHRoZSBBcmNociBvYmplY3QuCgpkZiA8LSByb3dEYXRhKHJuYV9vdmVybGFwKSAlPiUgYXMuZGF0YS5mcmFtZSgpICU+JSBmaWx0ZXIoZ2VuZSAlaW4lZ2VuZXMpCmRmCgpncmFuZ2VzIDwtIGRmICU+JSBHUmFuZ2VzKCkKZ3JhbmdlcwoKCmBgYAoKCldlIHdpbGwgbm93IGFkZCB0aGUgZ2VuZSBleHByZXNzaW9uIG1hdHJpeCB0byB0aGUgQXJjaFIgb2JqZWN0LiBGaXJzdCwgd2UgaGF2ZSB0byAKY29udmVydCB0aGUgU2luZ2xlIENlbGwgRXhwZXJpbWVudCB0byBhIFN1bW1hcml6ZWQgRXhwZXJpbWVudC4KCmBgYCN7cn0KIyBjcmVhdGUgc3VtbWFyaXplZCBleHBlcmltZW50IG9mIHRoZSBzY1JOQS1zZXEKc2VSTkEgPC0gU3VtbWFyaXplZEV4cGVyaW1lbnQoYXNzYXlzID0gbGlzdChjb3VudHM9cm5hX2NvdW50cyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IGNvbG5hbWVzKGZpbHRfcHJvY19ybmEpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHJvd1JhbmdlcyA9IGdyYW5nZXMpCnNlUk5BCgoKIyBhZGQgUk5BIGdlbmUgZXhwcmVzc2lvbiBtYXRyaXggdG8gdGhlIEFyY2hSIG9iamVjdAojZmlsdF9wcm9qIDwtIGFkZEdlbmVFeHByZXNzaW9uTWF0cml4KGlucHV0ID0gZmlsdF9wcm9qLCBzZVJOQSA9IHNlUk5BLCBmb3JjZSA9IFRSVUUpCgoKIyBzYXZlIHRoZSBBcmNoUiBvYmplY3Qgd2l0aCB0aGUgYWRkZWQgZ2VuZSBleHByZXNzaW9uIG1hdHJpeAojc2F2ZUFyY2hSUHJvamVjdChmaWx0X3Byb2osIG91dHB1dERpcmVjdG9yeSA9ICIwOF9BcmNoUm9iamVjdF9hbGxfdGltZXBvaW50c19hZGRlZF9nZW5lX2V4cHIvIiwgbG9hZCA9IEZBTFNFKQpgYGAKCgoKV2Ugd2lsbCBhZGQgYSBzZWNvbmQgTFNJIGZvciB0aGUgUk5BIGRhdGEuIFRoZSBvbmUgYmFzZWQgb24gdGhlIEFUQUMgZGF0YSBpcyBjYWxsZWQgCiJMU0lfQVRBQyIuIFdlIHdpbGwgY2FsbCB0aGUgb25lIGZvciB0aGUgUk5BIGRhdGEgIkxTSV9STkEiCgpgYGB7cn0KbWFya2VyX2dlbmVzIDwtIGMoIkxhbWIxIiwgIlBsYXQiLCAiU3BhcmMiLCAiRWxmNSIsICJBc2NsMiIsICJUZmFwMmMiLCAiVHRyIiwKICAgICAgICAgICAgICAgICAgIkFwb2EyIiwgIkFwb2UiLCAiQ3lzdG0xIiwgIkVtYiIsICJTcGluazEiLCAiQW1vdCIsICJLcnQxOSIpCmBgYAoKCmBgYCN7cn0KcCA8LSBwbG90RW1iZWRkaW5nKAogICAgQXJjaFJQcm9qID0gZmlsdF9wcm9qLCAKICAgIGNvbG9yQnkgPSAiR2VuZVNjb3JlTWF0cml4IiwgCiAgICBuYW1lID0gbWFya2VyX2dlbmVzLCAKICAgIGVtYmVkZGluZyA9ICJVTUFQIiwKICAgIHF1YW50Q3V0ID0gYygwLjAxLCAwLjk1KSwKICAgIGltcHV0ZVdlaWdodHMgPSBOVUxMCikKYGBgCgoKYGBgI3tyfQpwIDwtIHBsb3RFbWJlZGRpbmcoCiAgICBBcmNoUlByb2ogPSBmaWx0X3Byb2osIAogICAgY29sb3JCeSA9ICJHZW5lU2NvcmVNYXRyaXgiLCAKICAgIG5hbWUgPSBtYXJrZXJfZ2VuZXMsIAogICAgZW1iZWRkaW5nID0gIlVNQVAiLAogICAgcXVhbnRDdXQgPSBjKDAuMDEsIDAuOTUpLAogICAgaW1wdXRlV2VpZ2h0cyA9IE5VTEwKKQpgYGAKCgojIyMgVmlzdWFsaXplIHRoZSBnZW5lIGFjdGl2aXR5IHNjb3JlcwoKYGBge3J9CmhlYXRtYXAoYXMubWF0cml4KGdlbmVfc2NvcmVfbWF0WzE6NSwgMTo1XSkpCgpwcmludChwYXN0ZTAoIk1pbmltdW0gZ2VuZSBzY29yZTogIiwgbWluKGdlbmVfc2NvcmVfbWF0KSkpCnByaW50KHBhc3RlMCgiTWF4aW11bSBnZW5lIHNjb3JlOiAiLCBtYXgoZ2VuZV9zY29yZV9tYXQpKSkKCmBgYAoKYGBge3J9CnByaW50KHBhc3RlMCgiTnVtYmVyIG9mIGdlbmVzIGluIHRoZSBnZW5lIGFjdGl2aXR5IHNjb3JlIG1hdHJpeDogIiwgZGltKGdlbmVfc2NvcmVfbWF0KVtbMV1dKSkKYGBgCgoKYGBge3J9CnRpYmJsZShnZW5lID0gcm93bmFtZXMoZ2VuZV9zY29yZV9tYXQpLAogICAgICAgICAgbWVhbiA9IE1hdHJpeDo6cm93TWVhbnMoZ2VuZV9zY29yZV9tYXQpLAogICAgICAgICAgdmFyID0gTWF0cml4R2VuZXJpY3M6OnJvd1ZhcnMoZ2VuZV9zY29yZV9tYXQpKSAlPiUgCiAgbXV0YXRlKGxvZzEwX21lYW4gPSBsb2cxMChtZWFuKSwKICAgICAgICAgbG9nMTBfdmFyID0gbG9nMTAodmFyKSkgJT4lIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gbG9nMTBfbWVhbiwgeSA9IGxvZzEwX3ZhcikpICsKICBsYWJzKHRpdGxlID0gIkdlbmUgYWN0aXZpdHkgc2NvcmUgZnJvbSBzY0FUQUMtc2VxIikKCmBgYAoKCiMjIyBWaXN1YWxpemUgdGhlIHJhdyBnZW5lIGV4cHJlc3Npb24gY291bnRzCgpgYGB7cn0KcHJpbnQocGFzdGUwKCJOdW1iZXIgb2YgZ2VuZXMgaW4gZ2VuZSBleHByZXNzaW9uIG1hdHJpeDogIiwgZGltKHJuYV9jb3VudHMpW1sxXV0pKQpgYGAKCgpgYGB7cn0KdGliYmxlKGdlbmUgPSByb3duYW1lcyhybmFfY291bnRzKSwKICAgICAgICAgIG1lYW4gPSBNYXRyaXg6OnJvd01lYW5zKHJuYV9jb3VudHMpLAogICAgICAgICAgdmFyID0gTWF0cml4R2VuZXJpY3M6OnJvd1ZhcnMocm5hX2NvdW50cykpICU+JSAKICBtdXRhdGUobG9nMTBfbWVhbiA9IGxvZzEwKG1lYW4pLAogICAgICAgICBsb2cxMF92YXIgPSBsb2cxMCh2YXIpKSAlPiUgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBsb2cxMF9tZWFuLCB5ID0gbG9nMTBfdmFyKSkgKwogIGxhYnModGl0bGUgPSAiR2VuZSBleHByZXNzaW9uIHJhdyBjb3VudHMiKQpgYGAKCgpOb3JtYWxpemUgdGhlIFJOQSBjb3VudHMgdXNpbmcgbG9nMiB0cmFuc2Zvcm1hdGlvbjoKCjEuIG5vcm1hbGl6ZSBieSBsaWJyYXJ5IHNpemUKMi4gc2NhbGUgd2l0aCBmYWN0b3IgMTAwMAozLiBhZGQgcHNldWRvY291bnQKNC4gbG9nIHRyYW5zZm9ybSAKCmBgYCN7cn0Kbm9ybV9ybmEgPC0gTWF0cml4Ojp0KE1hdHJpeDo6dChybmFfY291bnRzKSAvCiAgICAgICAgICAgICAgICAgICAgICAgIE1hdHJpeDo6Y29sU3VtcyhybmFfY291bnRzKSkKCiNzY2FsaW5nIGZhY3RvciA9IDEwMDAKc2NfZmFjdG9yIDwtIDFlNAoKIyBhZGQgcHNldWRvY291bnQgb2YgMQpsb2dub3JtX3JuYSA8LSAobG9nKG5vcm1fcm5hICogc2NfZmFjdG9yICsgMSkpCmBgYAoKCmBgYCN7cn0KdGliYmxlKGdlbmUgPSByb3duYW1lcyhsb2dub3JtX3JuYSksCiAgICAgICAgICBtZWFuID0gTWF0cml4Ojpyb3dNZWFucyhsb2dub3JtX3JuYSksCiAgICAgICAgICB2YXIgPSByb3dWYXJzKGxvZ25vcm1fcm5hKSkgJT4lIAogIG11dGF0ZShsb2cxMF9tZWFuID0gbG9nMTAobWVhbiksCiAgICAgICAgIGxvZzEwX3ZhciA9IGxvZzEwKHZhcikpICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxvZzEwX21lYW4sIHkgPSBsb2cxMF92YXIpKSArCiAgbGFicyh0aXRsZSA9ICJHZW5lIGV4cHJlc3Npb24gbG9nLW5vcm1hbGl6ZWQgUk5BIGV4cHJlc3Npb24iKQpgYGAKCgoKIyMjIENvcnJlbGF0aW9uIGJldHdlZW4gZ2VuZSBhY3Rpdml0eSBzY29yZSBhbmQgZ2VuZSBleHByZXNzaW9uOgoKYGBgI3tyfQptYXJrZXJfZ2VuZXMgPC0gYygiTGFtYjEiLCAiUGxhdCIsICJTcGFyYyIsICJFbGY1IiwgIkFzY2wyIiwgIlRmYXAyYyIsICJUdHIiLAogICAgICAgICAgICAgICAgICAiQXBvYTIiLCAiQXBvZSIsICJDeXN0bTEiLCAiRW1iIiwgIlNwaW5rMSIsICJBbW90IiwgIktydDE5IikKYGBgCgoKYGBgI3tyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTV9CnBsb3RzIDwtIG1hcChtYXJrZXJfZ2VuZXMsIGZ1bmN0aW9uKG4pewpwbG90IDwtIHRpYmJsZShnZW5lX2V4cHIgPSBsb2dub3JtX3JuYVtuLCBdLCAKICAgICAgIGdlbmVfc2NvcmUgPSBnZW5lX3Njb3JlX21hdFtuLCBdKSAlPiUgCiAgZ2dwbG90KGFlcyggeCA9IGdlbmVfZXhwcix5ID0gZ2VuZV9zY29yZSkpICsgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fc21vb3RoKGZvcm11bGEgPSB5IH4geCwgbWV0aG9kID0gImxtIiwgc2l6ZT0wLjEpIAoKfSkKCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShncm9icyA9IHBsb3RzLCBuY29sID0gMykKCnRlc3QgPC0gY29yKHJuYV9jb3VudHNbIkFwb2UiLCBdLCBnZW5lX3Njb3JlX21hdFsiTGFtYjEiLCBdKQp0ZXN0CmBgYAoKYGBge3J9CgpgYGAKCgoKYGBgI3tyfQpwbG90IDwtIHRpYmJsZShnZW5lX2V4cHIgPSBsb2dub3JtX3JuYVsiTGFtYjEiLCBdLCAKICAgICAgIGdlbmVfc2NvcmUgPSBnZW5lX3Njb3JlX21hdFsiTGFtYjEiLCBdKSAlPiUgCiAgZ2dwbG90KGFlcyggeCA9IGdlbmVfZXhwcix5ID0gZ2VuZV9zY29yZSkpICsgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChmb3JtdWxhID0geSB+IHgsIG1ldGhvZCA9ICJsbSIsIHNpemU9MC4xKSAKICAjZ2dwdWJyOjpzdGF0X2NvcihhZXMobGFiZWwgPSAuLnIubGFiZWwuLiksIG1ldGhvZCA9ICJwZWFyc29uIiwgci5kaWdpdHM9MikKICAKcGxvdApgYGAKCgpgYGAje3J9CnRlc3QgPC0gQXJjaFI6OmdldE1hdHJpeEZyb21Qcm9qZWN0KGZpbHRfcHJvaiwgdXNlTWF0cml4ID0gIkdlbmVFeHByZXNzaW9uTWF0cml4IikKCmNvbERhdGEodGVzdCkgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIGNvbG5hbWVzKCkgJT4lIHRhaWwoMSkgJT4lIHByaW50KCkKCmZpbHRfcHJvaiA8LSBhZGRJdGVyYXRpdmVMU0koCiAgICBBcmNoUlByb2ogPSBmaWx0X3Byb2osIAogICAgY2x1c3RlclBhcmFtcyA9IGxpc3QoCiAgICAgIHJlc29sdXRpb24gPSAwLjIsIAogICAgICBzYW1wbGVDZWxscyA9IDEwMDAwLAogICAgICBuLnN0YXJ0ID0gMTAKICAgICksCiAgICBzYXZlSXRlcmF0aW9ucyA9IEZBTFNFLAogICAgdXNlTWF0cml4ID0gIkdlbmVFeHByZXNzaW9uTWF0cml4IiwgCiAgICBkZXB0aENvbCA9ICJHZXhfblVNSSIsCiAgICB2YXJGZWF0dXJlcyA9IDI1MDAsCiAgICBmaXJzdFNlbGVjdGlvbiA9ICJ2YXJpYWJsZSIsCiAgICBiaW5hcml6ZSA9IEZBTFNFLAogICAgbmFtZSA9ICJMU0lfUk5BIgopCkFyY2hSOjpnZXRDZWxsQ29sRGF0YShmaWx0X3Byb2opICU+JSBhcy5kYXRhLmZyYW1lKCkKYGBgCgoKYGBgI3tyfQpmaWx0X3Byb2ogPC0gYWRkSXRlcmF0aXZlTFNJKAogIEFyY2hSUHJvaiA9IGZpbHRfcHJvaiwgCiAgc2F2ZUl0ZXJhdGlvbnMgPSBGQUxTRSwKICBjbHVzdGVyUGFyYW1zICA9IGxpc3QoCiAgICByZXNvbHV0aW9uID0gMC4yLAogICAgc2FtcGxlQ2VsbHMgPSAxMDAwMCwKICAgIG4uc3RhcnQgPSAxMAogICksCiAgdXNlTWF0cml4ID0gIkdlbmVFeHByZXNzaW9uTWF0cml4IiwKICBkZXB0aENvbCA9ICJHZXhfblVNSSIsCiAgdmFyRmVhdHVyZXMgPSAxMDAwMDAsIAogIGZpcnN0U2VsZWN0aW9uID0gIlZhciIsIAogIGJpbmFyaXplID0gRkFMU0UsCiAgbmFtZSA9ICJMU0lfUk5BIgopCmBgYAoKCk5vdyB3ZSB3aWxsIGNvbWJpbmUgdGhlIHR3byBlbWJlZGRpbmdzLgoKYGBgI3tyfQojQ29tYmluZWQgRGltcwpwcm9qIDwtIGFkZENvbWJpbmVkRGltcyhwcm9qLCByZWR1Y2VkRGltcyA9IGMoIkxTSV9BVEFDIiwgIkxTSV9STkEiKSwgbmFtZSA9ICAiTFNJX0NvbWJpbmVkIikKCiNVTUFQcwpwcm9qIDwtIGFkZFVNQVAocHJvaiwgcmVkdWNlZERpbXMgPSAiTFNJX0FUQUMiLCBuYW1lID0gIlVNQVBfQVRBQyIsIG1pbkRpc3QgPSAwLjgsIGZvcmNlID0gVFJVRSkKCnByb2ogPC0gYWRkVU1BUChwcm9qLCByZWR1Y2VkRGltcyA9ICJMU0lfUk5BIiwgbmFtZSA9ICJVTUFQX1JOQSIsIG1pbkRpc3QgPSAwLjgsIGZvcmNlID0gVFJVRSkKCnByb2ogPC0gYWRkVU1BUChwcm9qLCByZWR1Y2VkRGltcyA9ICJMU0lfQ29tYmluZWQiLCBuYW1lID0gIlVNQVBfQ29tYmluZWQiLCBtaW5EaXN0ID0gMC44LCBmb3JjZSA9IFRSVUUpCgoKYGBgCgo=